AWS Developer Tools Blog
Using CMake Exports with the AWS SDK for C++
This is our very first C++ blog post for the AWS Developer blog. There will be more to come. We are excited to receive and share feedback with the C++ community. This first post will start where most projects start, with the building of a simple program.
Building an application in C++ can be a daunting task—especially when dependencies are involved. Even after you have figured out what you want to do and which libraries you need to use, you encounter seemingly endless, painful tasks to compile, link, and distribute your application.
AWS SDK for C++ users most frequently report the difficulty of compiling and linking against the SDK. This involves building the SDK, installing the header files and libraries somewhere, updating the build system of the application with the include and linker paths, and passing definitions to the compiler. This is an error-prone– and now unnecessary– process. CMake has built-in functionality that will handle this scenario. We have now updated the CMake build scripts to handle this complexity for you.
The example we will use in this post assumes you are familiar with Amazon Simple Storage Service (Amazon S3), and know how to download and build the SDK. For more information, see our readme on github. If we want to write a simple program to upload and retrieve objects from Amazon S3. The code would look something like this:
#include <aws/s3/S3Client.h>
#include <aws/s3/model/PutObjectRequest.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/core/Aws.h>
#include <aws/core/utils/memory/stl/AWSStringStream.h>
using namespace Aws::S3;
using namespace Aws::S3::Model;
static const char* KEY = "s3_cpp_sample_key";
static const char* BUCKET = "s3-cpp-sample-bucket";
int main()
{
Aws::SDKOptions options;
Aws::InitAPI(options);
{
S3Client client;
//first put an object into s3
PutObjectRequest putObjectRequest;
putObjectRequest.WithKey(KEY)
.WithBucket(BUCKET);
//this can be any arbitrary stream (e.g. fstream, stringstream etc...)
auto requestStream = Aws::MakeShared<Aws::StringStream>("s3-sample");
*requestStream << "Hello World!";
//set the stream that will be put to s3
putObjectRequest.SetBody(requestStream);
auto putObjectOutcome = client.PutObject(putObjectRequest);
if(putObjectOutcome.IsSuccess())
{
std::cout << "Put object succeeded" << std::endl;
}
else
{
std::cout << "Error while putting Object " << putObjectOutcome.GetError().GetExceptionName() <<
" " << putObjectOutcome.GetError().GetMessage() << std::endl;
}
//now get the object back out of s3. The response stream can be overridden here if you want it to go directly to
// a file. In this case the default string buf is exactly what we want.
GetObjectRequest getObjectRequest;
getObjectRequest.WithBucket(BUCKET)
.WithKey(KEY);
auto getObjectOutcome = client.GetObject(getObjectRequest);
if(getObjectOutcome.IsSuccess())
{
std::cout << "Successfully retrieved object from s3 with value: " << std::endl;
std::cout << getObjectOutcome.GetResult().GetBody().rdbuf() << std::endl << std::endl;;
}
else
{
std::cout << "Error while getting object " << getObjectOutcome.GetError().GetExceptionName() <<
" " << getObjectOutcome.GetError().GetMessage() << std::endl;
}
}
Aws::ShutdownAPI(options);
return 0;
}
Here, we have a direct dependency on aws-cpp-sdk-s3 and an indirect dependency on aws-cpp-sdk-core. Furthermore, we have several platform-specific dependencies that are required to make this work. On Windows, this involves WinHttp and BCrypt. On Linux, curl and OpenSSL. On OSX, curl and CommonCrypto. Other platforms, such as mobile, have their own dependencies. Traditionally, you would need to update your build system to detect each of these platforms and inject the right properties for each target.
However, the build process for the SDK already has access to this information from its configuration step. Why should you have to worry about this mess? Enter CMake export(). What would a CMakeLists.txt look like to build this program? This file generates our build artifacts for each platform we need to support—Visual Studio, XCode, AutoMake, and so on.
cmake_minimum_required(VERSION 2.8)
project(s3-sample)
#this will locate the aws sdk for c++ package so that we can use its targets
find_package(aws-sdk-cpp)
add_executable(s3-sample main.cpp)
#since we called find_package(), this will resolve all dependencies, header files, and cflags necessary
#to build and link your executable.
target_link_libraries(s3-sample aws-cpp-sdk-s3)
That’s all you need to build your program. When we run this script for Visual Studio, CMake will determine that aws-cpp-sdk-s3 has dependencies on aws-cpp-sdk-core, WinHttp, and BCrypt. Also, the CMake configuration for the aws-sdk-cpp package knows whether the SDK was built using custom memory management. It will make sure the –DAWS_CUSTOM_MEMORY_MANAGEMENT flag is passed to your compiler if needed. The resulting Visual Studio projects will already have the include and linker arguments set and will contain compile definitions that need to be passed to your compiler. On GCC and Clang, we will also go ahead and pass you the –std=c++11 flag.
To configure your project, simply run the following:
cmake –Daws-sdk-cpp_DIR=<path to your SDK build> <path to your source>
You can pass additional CMake arguments , such as –G “Visual Studio 12 2013 Win64” too.
Now you are ready to build with msbuild, make, or whatever other build system you are using.
Obviously, not everyone uses or even wants to use CMake. The aws-sdk-cpp-config.cmake file will contain all of the information required to update your build script to use the SDK.
We’d like to extend a special thanks to our GitHub users for requesting this feature, especially Rico Huijbers who shared a blog post on the topic. His original post can be found here
We are excited to be offering better support to the C++ community. We invite you to try this feature and leave feedback here or on GitHub.