diff --git a/ci/build-examples.yaml b/ci/build-examples.yaml index 41f527bd..84d32a5f 100644 --- a/ci/build-examples.yaml +++ b/ci/build-examples.yaml @@ -150,6 +150,16 @@ steps: 'hello-world-rooted', ] + - name: 'pack' + waitFor: ['gcf-builder-ready'] + id: 'howto-use-legacy-code' + args: ['build', + '--env', 'FUNCTION_SIGNATURE_TYPE=http', + '--env', 'TARGET_FUNCTION=HowtoUseLegacyCode', + '--path', 'examples/howto_use_legacy_code', + 'howto-use-legacy-code', + ] + # Build the cloud site examples. - name: 'pack' waitFor: ['gcf-builder-ready'] diff --git a/ci/generate-build-examples.sh b/ci/generate-build-examples.sh index 6fb5899f..d98ab3ca 100755 --- a/ci/generate-build-examples.sh +++ b/ci/generate-build-examples.sh @@ -134,6 +134,7 @@ generic_example hello_gcs HelloGcs http generic_example hello_with_third_party HelloWithThirdParty http generic_example hello_world HelloWorld http generic_example hello_world ::HelloWorld http hello-world-rooted +generic_example howto_use_legacy_code HowtoUseLegacyCode http howto-use-legacy-code cat <<_EOF_ # Build the cloud site examples. diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e7835da5..bf6d907b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,6 +32,8 @@ add_library( hello_multiple_sources/hello_multiple_sources.cc hello_with_third_party/hello_with_third_party.cc hello_world/hello_world.cc + howto_use_legacy_code/howto_use_legacy_code.cc + howto_use_legacy_code/legacy/legacy.cc site/bearer_token/bearer_token.cc site/concepts_after_response/concepts_after_response.cc site/concepts_after_timeout/concepts_after_timeout.cc diff --git a/examples/howto_use_legacy_code/CMakeLists.txt b/examples/howto_use_legacy_code/CMakeLists.txt new file mode 100644 index 00000000..09cb5428 --- /dev/null +++ b/examples/howto_use_legacy_code/CMakeLists.txt @@ -0,0 +1,45 @@ +# ~~~ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ~~~ + +cmake_minimum_required(VERSION 3.5) +project(functions-framework-cpp-howto-local-development CXX C) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Threads REQUIRED) +find_package(functions_framework_cpp REQUIRED) + +include(ExternalProject) +externalproject_add( + legacy + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/legacy" + DEPENDS "" SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/legacy" + CONFIGURE_COMMAND "" + BUILD_COMMAND "make" "CXX=${CMAKE_CXX_COMPILER}" "default" BUILD_IN_SOURCE + ON + BUILD_BYPRODUCTS "/liblegacy.a" + INSTALL_COMMAND "") +ExternalProject_Get_Property(legacy SOURCE_DIR) + +add_library(functions_framework_cpp_function howto_use_legacy_code.cc) +add_dependencies(functions_framework_cpp_function legacy) +target_link_libraries( + functions_framework_cpp_function PUBLIC "${SOURCE_DIR}/liblegacy.a" + functions-framework-cpp::framework) + +add_executable(main main.cc) +target_link_libraries(main PRIVATE functions_framework_cpp_function) diff --git a/examples/howto_use_legacy_code/README.md b/examples/howto_use_legacy_code/README.md new file mode 100644 index 00000000..3e53580b --- /dev/null +++ b/examples/howto_use_legacy_code/README.md @@ -0,0 +1,148 @@ +# How-to Guide: use functions with legacy code + +This guide shows you how to use legacy code with the C++ Functions Framework. + +This guide shows how to create a CMake file that invokes an external build tool +to compile some legacy code. This is a complex subject, with a lot of nuances +depending on the nature of the legacy code and the build system used to compile +it. We hope this guide can give you starting pointers to learn more about the +topic. + +We recommend you use [ExternalProject_Add] facility in CMake to compile any +pre-existing code. This is a general-purpose function in CMake that invokes +any external tool to compile some code. Often this function also downloads +the code from some external repository, but it can use code in your source +tree. + +[ExternalProject_Add]: https://cmake.org/cmake/help/latest/module/ExternalProject.html + +## Installing Dependencies + +Because the Functions Framework for C++ uses C++17, you will need a working C++ +compiler with support for C++17. If you are a GCC user, any version after 8.0 +should work. For Clang any version after 6.0 should work. We have not tested +these instructions with MSVC. + +This guide uses [CMake (>= 3.5)][cmake] as a build tool. There are detailed +[install instructions][cmake-install], but many system package managers have +packages for it. Verify your CMake tool version: + +```shell +cmake --version +# Output: cmake version X.Y.Z ... verify this is >= 3.5 +``` + +The Functions Framework for C++ recommends using [vcpkg][vcpkg-gh] to manage +dependencies. This is how dependencies will be installed and compiled in the +production environment, so you probably want to use the same approach in +development. Follow the [vcpkg install instructions][vcpkg-install] to get +vcpkg installed on your development environment. For example, on Linux you +would use: + +```shell +cd $HOME +git clone -q https://github.com/microsoft/vcpkg +# Expected output: none +./vcpkg/bootstrap-vcpkg.sh --disableMetrics +``` + +You should see output like this: + +```console +Downloading cmake... +... +Downloading ninja... +... +Downloading vcpkg tool sources +... +.. +Building vcpkg-tool... +... +.. +[88/88] Linking CXX executable vcpkg +``` + +This will create a `vcpkg` executable in the `$HOME/vcpkg` directory. + +## Compiling a function + +Once vcpkg is compiled you can build the example. If you have not cloned +the `functions-framework-cpp` repository yet, you need to do so now: + +```shell +cd $HOME +git clone -q https://github.com/GoogleCloudPlatform/functions-framework-cpp.git +# Expected output: none +``` + +Then go to the directory hosting this example and use CMake to build +the example: + +```shell +cd functions-framework-cpp/examples/howto_use_legacy_code +``` + +Run the CMake configure step. If needed, this will download and build any +dependencies for your function: + +```shell +cmake -H. -B.build -DCMAKE_TOOLCHAIN_FILE=$HOME/vcpkg/scripts/buildsystems/vcpkg.cmake +``` + +You should see output like this: + +> :warning: the first time you use or build the framework it might take several +> minutes, maybe as much as 1/2 hour, depending on your workstation +> performance. Future builds, even in different directories, should be faster +> as `vcpkg` caches binary artifacts in `$HOME/.cache/vcpkg`. + +```console +-- Running vcpkg install +... +-- Running vcpkg install - done +-- Configuring done +-- Generating done +-- Build files have been written to: .../howto_use_legacy_code/.build +``` + +If you have previously built the dependencies with `vcpkg` the output may include +informational messages about reusing these binary artifacts. With some versions +of CMake you may see a warning about your version of Boost, something like: + +```console +CMake Warning at /usr/share/cmake-3.16/Modules/FindBoost.cmake:1161 (message): + New Boost version may have incorrect or missing dependencies and imported + targets +``` + +You can safely ignore this warning. + +With the dependencies built and CMake configured you can now build the code: + +```shell +cmake --build .build +``` + +This will output some informational messages about the build progress, and will +create a binary called `local_server` in the .build subdirectory. + +## Running the function locally + +This will produce a standalone HTTP server, which you can run locally using: + +```shell +.build/main --port 8080 +``` + +Test this program using `curl`. In a separate terminal window run: + +```shell +curl http://localhost:8080 +``` + +You can just interrupt (`Ctrl-C`) the program to terminate it. + +[vcpkg-gh]: https://github.com/microsoft/vcpkg +[vcpkg-install]: https://github.com/microsoft/vcpkg#getting-started +[cmake]: https://cmake.org +[cmake-install]: https://cmake.org/install/ diff --git a/examples/howto_use_legacy_code/howto_use_legacy_code.cc b/examples/howto_use_legacy_code/howto_use_legacy_code.cc new file mode 100644 index 00000000..9ff6e155 --- /dev/null +++ b/examples/howto_use_legacy_code/howto_use_legacy_code.cc @@ -0,0 +1,28 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "legacy/legacy.h" +#include +#include + +using ::google::cloud::functions::HttpRequest; +using ::google::cloud::functions::HttpResponse; + +// Though not used in this example, the request is passed by value to support +// applications that move-out its data. +HttpResponse HowtoUseLegacyCode(HttpRequest) { // NOLINT + return HttpResponse{} + .set_header("Content-Type", "text/plain") + .set_payload(legacy::LegacyMessage()); +} diff --git a/examples/howto_use_legacy_code/legacy/.gitignore b/examples/howto_use_legacy_code/legacy/.gitignore new file mode 100644 index 00000000..302a4450 --- /dev/null +++ b/examples/howto_use_legacy_code/legacy/.gitignore @@ -0,0 +1,2 @@ +liblegacy.a +legacy.o diff --git a/examples/howto_use_legacy_code/legacy/Makefile b/examples/howto_use_legacy_code/legacy/Makefile new file mode 100644 index 00000000..8b967b14 --- /dev/null +++ b/examples/howto_use_legacy_code/legacy/Makefile @@ -0,0 +1,23 @@ +# ~~~ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ~~~ + +default: liblegacy.a + +liblegacy.a: legacy.o + $(AR) r $@ $< + +legacy.o: legacy.cc legacy.h + $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/examples/howto_use_legacy_code/legacy/legacy.cc b/examples/howto_use_legacy_code/legacy/legacy.cc new file mode 100644 index 00000000..dffeff1c --- /dev/null +++ b/examples/howto_use_legacy_code/legacy/legacy.cc @@ -0,0 +1,22 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "legacy.h" +#include + +namespace legacy { +std::string LegacyMessage() { + return "C++ version is " + std::to_string(__cplusplus) + "\n"; +} +} // namespace legacy diff --git a/examples/howto_use_legacy_code/legacy/legacy.h b/examples/howto_use_legacy_code/legacy/legacy.h new file mode 100644 index 00000000..ce465f43 --- /dev/null +++ b/examples/howto_use_legacy_code/legacy/legacy.h @@ -0,0 +1,24 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FUNCTIONS_FRAMEWORK_CPP_EXAMPLES_HOWTO_USE_LEGACY_CODE_LEGACY_LEGACY_H +#define FUNCTIONS_FRAMEWORK_CPP_EXAMPLES_HOWTO_USE_LEGACY_CODE_LEGACY_LEGACY_H + +#include + +namespace legacy { +std::string LegacyMessage(); +} // namespace legacy + +#endif // FUNCTIONS_FRAMEWORK_CPP_EXAMPLES_HOWTO_USE_LEGACY_CODE_LEGACY_LEGACY_H diff --git a/examples/howto_use_legacy_code/main.cc b/examples/howto_use_legacy_code/main.cc new file mode 100644 index 00000000..28197c0d --- /dev/null +++ b/examples/howto_use_legacy_code/main.cc @@ -0,0 +1,24 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +namespace gcf = ::google::cloud::functions; + +gcf::HttpResponse HowtoUseLegacyCode(gcf::HttpRequest); + +int main(int argc, char* argv[]) { + return ::google::cloud::functions::Run(argc, argv, HowtoUseLegacyCode); +} diff --git a/examples/howto_use_legacy_code/vcpkg.json b/examples/howto_use_legacy_code/vcpkg.json new file mode 100644 index 00000000..42cde824 --- /dev/null +++ b/examples/howto_use_legacy_code/vcpkg.json @@ -0,0 +1,8 @@ +{ + "name": "hello-with-third-party", + "version-string": "unversioned", + "dependencies": [ + "fmt", + "functions-framework-cpp" + ] +} diff --git a/examples/http_examples_test.cc b/examples/http_examples_test.cc index 58af27e8..38e37d89 100644 --- a/examples/http_examples_test.cc +++ b/examples/http_examples_test.cc @@ -22,6 +22,7 @@ gcf::HttpResponse HelloGcs(gcf::HttpRequest); gcf::HttpResponse HelloMultipleSources(gcf::HttpRequest); gcf::HttpResponse HelloWithThirdParty(gcf::HttpRequest request); gcf::HttpResponse HelloWorld(gcf::HttpRequest); +gcf::HttpResponse HowtoUseLegacyCode(gcf::HttpRequest); namespace hello_from_namespace { gcf::HttpResponse HelloWorld(gcf::HttpRequest); @@ -67,4 +68,9 @@ TEST(HttpExamplesTest, HelloFromNestedNamespace) { EXPECT_THAT(actual.payload(), HasSubstr("C++ namespace")); } +TEST(HttpExampleTest, HowtoUseLegacyCode) { + auto const actual = HowtoUseLegacyCode(gcf::HttpRequest{}); + EXPECT_THAT(actual.payload(), HasSubstr("C++")); +} + } // namespace