Merge branch 'master'

Conflicts:
	CMakeLists.txt
	examples/CMakeLists.txt
	include/crow/http_connection.h
	tests/CMakeLists.txt
This commit is contained in:
The-EDev 2021-01-21 06:56:02 +03:00
commit 78ee97124b
14 changed files with 398 additions and 120 deletions

3
.gitignore vendored
View File

@ -47,3 +47,6 @@ html/
*.cxxflags
*.files
*.includes
#VS-Code
.vscode

View File

@ -1,71 +1,77 @@
cmake_minimum_required(VERSION 3.15)
project (crow_all)
#####################################
# Define Project-Wide Settings
#####################################
cmake_minimum_required(VERSION 3.15.0 FATAL_ERROR)
# Define the Project Name and Description
project (crow_all LANGUAGES CXX)
# Define the module path
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
find_package(Tcmalloc)
find_package(Threads)
find_package(OpenSSL)
find_package(ZLIB REQUIRED)
if(OPENSSL_FOUND)
include_directories(${OPENSSL_INCLUDE_DIR})
endif()
include_directories(${ZLIB_INCLUDE_DIR})
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
message("Found ccache ${CCACHE_FOUND}")
message("Using ccache to speed up compilation")
set(ENV{CCACHE_CPP2} "yes")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)
# Set required C++ Standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE "Release")
endif()
if (MSVC)
set(Boost_USE_STATIC_LIBS "On")
find_package( Boost 1.52 COMPONENTS system thread regex REQUIRED )
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++14 -pedantic -Wextra")
find_package( Boost 1.52 COMPONENTS system thread REQUIRED )
#####################################
# Define Options
#####################################
option(BUILD_EXAMPLES "Builds the examples in the project" ON)
option(BUILD_TESTING "Builds the tests in the project" ON)
#####################################
# Define CMake Module Imports
#####################################
include(${CMAKE_SOURCE_DIR}/cmake/dependencies.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/compiler_options.cmake)
#####################################
# Define project-wide imports
#####################################
# this can be alternatively (and as recommended way) done with target_include_directories()
if(BUILD_EXAMPLES OR BUILD_TESTING)
set(PROJECT_INCLUDE_DIR
${CMAKE_SOURCE_DIR}/include
)
include_directories("${PROJECT_INCLUDE_DIR}")
include_directories("${CMAKE_SOURCE_DIR}")
include_directories("${CMAKE_CURRENT_BINARY_DIR}") # To include crow_all.h
endif()
include_directories(${Boost_INCLUDE_DIR})
set(PROJECT_INCLUDE_DIR
${PROJECT_SOURCE_DIR}/include
#####################################
# Define Targets
#####################################
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
COMMAND python ${CMAKE_SOURCE_DIR}/scripts/merge_all.py
${CMAKE_SOURCE_DIR}/include
${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${CMAKE_SOURCE_DIR}/include/*.h ${CMAKE_SOURCE_DIR}/include/crow/*.h ${CMAKE_SOURCE_DIR}/include/crow/middlewares/*.h
)
include_directories("${PROJECT_INCLUDE_DIR}")
include_directories("${PROJECT_SOURCE_DIR}")
include_directories("${CMAKE_CURRENT_BINARY_DIR}") # To include crow_all.h
# Amalgamation
add_custom_target(amalgamation ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h)
add_subdirectory(examples)
# Examples
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if (MSVC)
else()
# Tests
if (NOT MSVC AND BUILD_TESTING)
add_subdirectory(tests)
enable_testing()
add_test(NAME crow_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/unittest)
add_test(NAME template_test COMMAND ${CMAKE_CURRENT_BINARY_DIR}/tests/template/test.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/template)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
COMMAND python ${PROJECT_SOURCE_DIR}/scripts/merge_all.py
${PROJECT_SOURCE_DIR}/include
${CMAKE_CURRENT_BINARY_DIR}/crow_all.h
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${PROJECT_SOURCE_DIR}/include/*.h ${PROJECT_SOURCE_DIR}/include/crow/*.h ${PROJECT_SOURCE_DIR}/include/crow/middlewares/*.h
)
add_custom_target(amalgamation ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h)
endif()
#####################################
# Install Files
#####################################
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/crow_all.h DESTINATION include)

View File

@ -0,0 +1,22 @@
# Compiler options with hardening flags
if(MSVC)
list(APPEND compiler_options
/W4
/permissive-
$<$<CONFIG:RELEASE>:/O2 /Ob2>
$<$<CONFIG:MINSIZEREL>:/O1 /Ob1>
$<$<CONFIG:RELWITHDEBINFO>:/Zi /O2 /Ob1>
$<$<CONFIG:DEBUG>:/Zi /Ob0 /Od /RTC1>)
else(MSVC)
list(APPEND compiler_options
-Wall
-Wextra
-Wpedantic
$<$<CONFIG:RELEASE>:-O2>
$<$<CONFIG:DEBUG>:-O0 -g -p -pg>)
endif()

35
cmake/dependencies.cmake Normal file
View File

@ -0,0 +1,35 @@
# Dependencies
if(BUILD_EXAMPLES OR BUILD_TESTING)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
message("Found ccache ${CCACHE_FOUND}")
message("Using ccache to speed up compilation")
set(ENV{CCACHE_CPP2} "yes")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)
find_package(Tcmalloc)
find_package(Threads)
if (MSVC)
set(Boost_USE_STATIC_LIBS ON)
find_package( Boost 1.64.0 COMPONENTS system thread regex REQUIRED )
else()
find_package( Boost 1.64.0 COMPONENTS system thread REQUIRED )
endif()
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIR})
endif()
endif()
if(BUILD_EXAMPLES)
# OpenSSL is needed at runtime dynamically by some examples
# if it isn't installed, the examples won't be built
find_package(OpenSSL)
if(OPENSSL_FOUND)
include_directories(${OPENSSL_INCLUDE_DIR})
endif()
endif()

View File

@ -1 +1,61 @@
***HELP NEEDED***
You can set Crow up behind any HTTP proxy of your liking, but we will be focusing specifically on 2 of the most popular web server software solutions, Apache2 and Nginx.<br><br>
A reverse proxy allows you to use Crow without exposing it directly to the internet. It also allows you to, for example, have crow run on a certain specific domain name, subdomain, or even a path, such as `domain.abc/crow`.<br><br>
We advise that you set crow up behind some form of reverse proxy if you plan on running a production Crow server that isn't local.<br>
!!! warning "SSL"
When using a proxy, make sure that you **do not** compile Crow with SSL enabled. SSL should be handled by the proxy.
##Apache2
Assuming you have both Apache2 and the modules [proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html), [proxy_http](https://httpd.apache.org/docs/2.4/mod/mod_proxy_http.html), [proxy_html](https://httpd.apache.org/docs/2.4/mod/mod_proxy_html.html) (if you plan on serving html pages), and [proxy_wstunnel](https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html) (if you plan on using websockets). You will need to enable those modules, which you can do using the following commands:
```sh
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_html
a2enmod proxy_wstunnel
```
Next up you'll need to change your configuration (default is `/etc/apache2/sites-enabled/000-default.conf`) and add the following lines (replace `localhost` and `40080` with the address and port you defined for your Crow App):
```
ProxyPass / http://localhost:40080
ProxyPassReverse / http://localhost:40080
```
If you want crow to run in a subdirectory (such as `domain.abc/crow`) you can use the `location` tag:
```
<Location "/crow">
ProxyPass http://localhost:40080
ProxyPassReverse http://localhost:40080
</Location>
```
!!! note
If you're using an Arch Linux based OS. You will have to access `/etc/httpd/conf/httpd.conf` to enable modules and change configuration
##Nginx
Setting Nginx up is slightly simpler than Apache, all you need is the Nginx package itself. Once you've installed it, go to the configuration file (usually a `.conf` file located in `/etc/nginx`) and add the following lines to your server section (replace `localhost` and `40080` with the address and port you defined for your Crow App):
```
location / {
proxy_pass http://localhost:40080/;
proxy_http_version 1.1;
}
```
Remember to remove or comment out any existing `location /` section.<br><br>
Alternatively, if you want to use a subdirectory, you can simply change the location parameter as such:
```
location /crow/ {
proxy_pass http://localhost:40080/;
proxy_http_version 1.1;
}
```

View File

@ -1,3 +1,36 @@
https://www.howtogeek.com/687970/how-to-run-a-linux-program-at-startup-with-systemd/
Using Systemd allows you to run any executable or script when the system starts. This can be useful when you don't want to re-run your Crow application every single time you restart your server.<br><br>
***HELP NEEDED***
##Writing the Service Unit File
In order to have Systemd recognize your application, you need to create a `.service` file that explains how Systemd should handle your program.<br><br>
To create a service file, you need to go to `/etc/systemd/system` and create an empty text file with the extension `.service`, the file name can be anything.<br><br>
Once the file is created, open it using your favorite text editor and add the following:
```sh
[Unit]
Description=My revolutionary Crow application
Wants=network.target
After=syslog.target network-online.target
[Service]
Type=simple
ExecStart=/absolute/path/to/your/executable
Restart=on-failure
RestartSec=10
KillMode=process
[Install]
WantedBy=multi-user.target
```
You will then need to give the correct permission, this can be done by using the following command (a `sudo` maybe required):
```sh
chmod 640 /etc/systemd/system/crowthing.service
```
And that's it! You can now use your `systemctl` controls to `enable`, `start`, `stop`, or `disable` your Crow application.<br><br>
If you're not familiar with Systemd, `systemctl enable crowthing.service` will allow your Crow application to run at startup, `start` will start it, and the rest is simple.

77
docs/guides/testing.md Normal file
View File

@ -0,0 +1,77 @@
Unit tests can be written in 2 ways for a Crow application.<br><br>
##The handler method
Crow Allows users to handle requests that may not come from the network. This is done by calling the `handle(req, res)` method and providing a request and response objects. Which causes crow to identify and run the appropriate handler, returning the resulting response.
```cpp linenums="1"
CROW_ROUTE(app, "/place")
([] { return "hi"; });
app.validate(); //Used to make sure all the route handlers are in order.
{
request req;
response res;
req.url = "/place";
app.handle(req, res); //res will contain a code of 200, and a response body of "hi"
}
```
!!! note
This method is the simpler of the two and is usually all you really need to test your routes.
!!! warning
This method does not send any data, nor does it run any post handle code, so things like static file serving (as far as sending the actual data) or compression cannot be tested using this method.
##The client method
This method involves creating a simple [ASIO](https://think-async.com/Asio/) client that sends the request and receives the response. It is considerably more complex than the earlier method, but it is both more realistic and includes post handle operations.
```cpp linenums="1"
static char buf[2048];
SimpleApp app;
CROW_ROUTE(app, "/")([] { return "A"; });
auto _ = async(launch::async,[&] { app1.bindaddr("127.0.0.1").port(45451).run(); });
app.wait_for_server_start();
std::string sendmsg = "GET /\r\nContent-Length:3\r\nX-HeaderTest: 123\r\n\r\nA=B\r\n";
asio::io_service is;
{
asio::ip::tcp::socket c(is);
c.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 45451));
c.send(asio::buffer(sendmsg));
size_t recved = c.receive(asio::buffer(buf, 2048));
CHECK('A' == buf[recved - 1]); //This is specific to catch2 testing library, but it should give a general idea of how to read the response.
}
app.stop(); //THIS MUST RUN
}
```
The first part is straightforward, create an app and add a route.<br>
The second part is launching the app asynchronously and waiting until it starts.<br>
The third is formulating our HTTP request string, the format is:
```
METHOD /
Content-Length:123
header1:value1
header2:value2
BODY
```
Next an `io_service` is created, then a TCP socket is created with the `io_service` and is connected to the application.<br>
Then send the HTTP request string through the socket inside a buffer, and read the result into the buffer in `line 1`.<br>
Finally check the result against the expected one.
!!! warning
Be absolutely sure that the line `app.stop()` runs, whether the test fails or succeedes. Not running it WILL CAUSE OTHER TESTS TO FAIL AND THE TEST TO HANG UNTIL THE PROCESS IS TERMINATED.

View File

@ -1,76 +1,75 @@
cmake_minimum_required(VERSION 3.15)
project (crow_examples)
# Define Required libraries
list(APPEND REQUIRED_LIBRARIES
${Boost_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${ZLIB_LIBRARIES}
)
if (MSVC)
add_executable(example_vs example_vs.cpp)
target_link_libraries(example_vs ${Boost_LIBRARIES})
target_link_libraries(example_vs ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(example_vs ${ZLIB_LIBRARIES})
target_compile_options(example_vs PRIVATE "${compiler_options}")
target_link_libraries(example_vs )
else ()
add_executable(helloworld helloworld.cpp)
target_link_libraries(helloworld ${Boost_LIBRARIES})
target_link_libraries(helloworld ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(helloworld ${ZLIB_LIBRARIES})
target_compile_options(helloworld PRIVATE "${compiler_options}")
target_link_libraries(helloworld PUBLIC ${REQUIRED_LIBRARIES})
add_executable(example_static_file example_static_file.cpp)
target_link_libraries(example_static_file ${Boost_LIBRARIES})
target_link_libraries(example_static_file ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(example_static_file ${ZLIB_LIBRARIES})
add_executable(example_compression example_compression.cpp)
target_compile_options(example_compression PRIVATE "${compiler_options}")
target_link_libraries(example_compression ${REQUIRED_LIBRARIES})
add_executable(example_compression example_compression.cpp)
target_link_libraries(example_compression ${Boost_LIBRARIES})
target_link_libraries(example_compression ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(example_compression ${ZLIB_LIBRARIES})
if (OPENSSL_FOUND)
# If OpenSSL is not found, the example won't be built
if (OPENSSL_FOUND)
add_executable(example_ssl ssl/example_ssl.cpp)
target_link_libraries(example_ssl ${Boost_LIBRARIES})
target_link_libraries(example_ssl ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES})
target_link_libraries(example_ssl ${ZLIB_LIBRARIES})
endif()
target_compile_options(example_ssl PRIVATE "${compiler_options}")
target_link_libraries(example_ssl PUBLIC ${REQUIRED_LIBRARIES} ${OPENSSL_LIBRARIES})
else()
message(STATUS "example_ssl Example deactivated - OpenSSL was not found")
endif()
add_executable(example_websocket websocket/example_ws.cpp)
target_link_libraries(example_websocket ${Boost_LIBRARIES})
target_link_libraries(example_websocket ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES})
target_link_libraries(example_websocket ${ZLIB_LIBRARIES})
add_custom_command(OUTPUT ws.html
add_executable(example_websocket websocket/example_ws.cpp)
target_compile_options(example_websocket PRIVATE "${compiler_options}")
target_link_libraries(example_websocket )
target_link_libraries(example_websocket PUBLIC ${REQUIRED_LIBRARIES})
add_custom_command(OUTPUT ws.html
COMMAND ${CMAKE_COMMAND} -E
copy ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html ${CMAKE_CURRENT_BINARY_DIR}/templates/ws.html
DEPENDS ${PROJECT_SOURCE_DIR}/websocket/templates/ws.html
)
add_custom_target(example_ws_copy ALL DEPENDS ws.html)
)
add_custom_target(example_ws_copy ALL DEPENDS ws.html)
add_executable(basic_example example.cpp)
target_link_libraries(basic_example ${Boost_LIBRARIES})
target_link_libraries(basic_example ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(basic_example ${ZLIB_LIBRARIES})
add_executable(basic_example example.cpp)
target_compile_options(basic_example PRIVATE "${compiler_options}")
target_link_libraries(basic_example PUBLIC ${REQUIRED_LIBRARIES})
if (Tcmalloc_FOUND)
if (Tcmalloc_FOUND)
target_link_libraries(basic_example ${Tcmalloc_LIBRARIES})
endif(Tcmalloc_FOUND)
endif(Tcmalloc_FOUND)
add_executable(example_with_all example_with_all.cpp)
add_dependencies(example_with_all amalgamation)
target_link_libraries(example_with_all ${Boost_LIBRARIES})
target_link_libraries(example_with_all ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(example_with_all ${ZLIB_LIBRARIES})
add_executable(example_with_all example_with_all.cpp)
add_dependencies(example_with_all amalgamation)
target_compile_options(example_with_all PRIVATE "${compiler_options}")
target_link_libraries(example_with_all PUBLIC ${REQUIRED_LIBRARIES})
add_custom_command(OUTPUT example_test.py
add_custom_command(OUTPUT example_test.py
COMMAND ${CMAKE_COMMAND} -E
copy ${PROJECT_SOURCE_DIR}/example_test.py ${CMAKE_CURRENT_BINARY_DIR}/example_test.py
DEPENDS ${PROJECT_SOURCE_DIR}/example_test.py
)
add_custom_target(example_copy ALL DEPENDS example_test.py)
)
add_custom_target(example_copy ALL DEPENDS example_test.py)
add_executable(example_chat example_chat.cpp)
target_link_libraries(example_chat ${Boost_LIBRARIES})
target_link_libraries(example_chat ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(example_chat ${ZLIB_LIBRARIES})
add_custom_command(OUTPUT example_chat.html
add_executable(example_chat example_chat.cpp)
target_compile_options(example_chat PRIVATE "${compiler_options}")
target_link_libraries(example_chat PUBLIC ${REQUIRED_LIBRARIES})
add_custom_command(OUTPUT example_chat.html
COMMAND ${CMAKE_COMMAND} -E
copy ${PROJECT_SOURCE_DIR}/example_chat.html ${CMAKE_CURRENT_BINARY_DIR}/example_chat.html
DEPENDS ${PROJECT_SOURCE_DIR}/example_chat.html
)
add_custom_target(example_chat_copy ALL DEPENDS example_chat.html)
)
add_custom_target(example_chat_copy ALL DEPENDS example_chat.html)
endif()

View File

@ -357,7 +357,6 @@ namespace crow
(*middlewares_, ctx_, req_, res);
}
std::string accept_encoding = req_.get_header_value("Accept-Encoding");
if (!accept_encoding.empty() && res.compressed)
{
@ -380,6 +379,17 @@ namespace crow
default:
break;
}
//if there is a redirection with a partial URL, treat the URL as a route.
std::string location = res.get_header_value("Location");
if (!location.empty() && location.find("://", 0) == std::string::npos)
{
#ifdef CROW_ENABLE_SSL
location.insert(0, "https://" + req_.get_header_value("Host"));
#else
location.insert(0, "http://" + req_.get_header_value("Host"));
#endif
res.set_header("location", location);
}
prepare_buffers();
@ -415,8 +425,11 @@ namespace crow
{300, "HTTP/1.1 300 Multiple Choices\r\n"},
{301, "HTTP/1.1 301 Moved Permanently\r\n"},
{302, "HTTP/1.1 302 Moved Temporarily\r\n"},
{302, "HTTP/1.1 302 Found\r\n"},
{303, "HTTP/1.1 303 See Other\r\n"},
{304, "HTTP/1.1 304 Not Modified\r\n"},
{307, "HTTP/1.1 307 Temporary Redirect\r\n"},
{308, "HTTP/1.1 308 Permanent Redirect\r\n"},
{400, "HTTP/1.1 400 Bad Request\r\n"},
{401, "HTTP/1.1 401 Unauthorized\r\n"},

View File

@ -26,8 +26,8 @@ namespace crow
struct request
{
HTTPMethod method;
std::string raw_url; ///< The full URL containing the host.
std::string url; ///< The Endpoint.
std::string raw_url; ///< The full URL containing the `?` and URL parameters.
std::string url; ///< The endpoint without any parameters.
query_string url_params; ///< The parameters associated with the request. (everything after the `?`)
ci_map headers;
std::string body;

View File

@ -100,7 +100,37 @@ namespace crow
completed_ = false;
}
/// Return a "Temporary Redirect" response.
///
/// Location can either be a route or a full URL.
void redirect(const std::string& location)
{
code = 307;
set_header("Location", location);
}
/// Return a "Permanent Redirect" response.
///
/// Location can either be a route or a full URL.
void redirect_perm(const std::string& location)
{
code = 308;
set_header("Location", location);
}
/// Return a "Found (Moved Temporarily)" response.
///
/// Location can either be a route or a full URL.
void moved(const std::string& location)
{
code = 302;
set_header("Location", location);
}
/// Return a "Moved Permanently" response.
///
/// Location can either be a route or a full URL.
void moved_perm(const std::string& location)
{
code = 301;
set_header("Location", location);

View File

@ -18,6 +18,7 @@ theme:
repo: fontawesome/brands/github-square
markdown_extensions:
- admonition
- pymdownx.highlight
- pymdownx.superfences
- pymdownx.inlinehilite
@ -40,6 +41,7 @@ nav:
- SSL: guides/ssl.md
- Static Files: guides/static.md
- Websockets: guides/websockets.md
- Writing Tests: guides/testing.md
- Server setup:
- Proxies: guides/proxies.md
- Systemd run on startup: guides/syste.md

View File

@ -6,10 +6,9 @@ set(TEST_SRCS
)
add_executable(unittest ${TEST_SRCS})
target_link_libraries(unittest ${Boost_LIBRARIES})
target_link_libraries(unittest ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(unittest ${ZLIB_LIBRARIES})
set_target_properties(unittest PROPERTIES COMPILE_FLAGS "-Wall -Werror -std=c++14")
target_link_libraries(unittest ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${ZLIB_LIBRARIES})
# set target compile options as defined in the cmake/compiler_options.cmake Module
target_compile_options(unittest PRIVATE ${compiler_options})
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set_target_properties(unittest PROPERTIES COMPILE_FLAGS "--coverage -fprofile-arcs -ftest-coverage")

View File

@ -10,8 +10,7 @@ set(TEST_SRCS
)
add_executable(mustachetest ${TEST_SRCS})
set_target_properties(mustachetest PROPERTIES COMPILE_FLAGS "-Wall -Werror -std=c++14")
target_compile_options(mustachetest PRIVATE "${compiler_options}")
file(COPY DIRECTORY . DESTINATION ${CMAKE_CURRENT_BINARY_DIR} FILES_MATCHING PATTERN "*.json")
add_custom_command(OUTPUT test.py