Skip to content

Commit

Permalink
CMake build and test. (#303)
Browse files Browse the repository at this point in the history
* CMake build and test.

Not supported yet:
* Windows
* Emscripten
* Compilers other than GCC and Clang.

* Fixed CMake issue where GLOBAL_OUTPUT_PATH was a bool.

* Added CMake build and test to Travis.

* Ignore .DS_Store

* Use CMake 2.8.7 as the minimum version.

Travis does not support CMake 3.0.

* Fix googletest. CMake stdlib build.

* Download googletest at CMake generation time. This makes linking
simpler and should fix the Linux Travis build.

* Build the jsonnet standard library header (std.jsonnet.h) via CMake.
Doing this in a cross-platform fashion required a small C++ utility to
generate the code.

* Added header files to targets.

CMake will compile targets without header files listed as files, but it won't recompile when changes are made unless the files are listed as dependencies.

* Added regression test CMake target.

* Fix cmake build errors.

* Add some documentation to the CMake scripts.
  • Loading branch information
spencels authored and sparkprime committed Oct 11, 2017
1 parent cb50389 commit 30f4739
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
**/.*.swp
**/.*.swo
**/*.o
**/*.a
**/*.dylib
*.dSYM

jsonnet
Expand All @@ -27,6 +29,21 @@ bazel-*
**/*.tfstate.backup

build/
external/
dist/
jsonnet.egg-info/

# Cmake
**/CMakeCache.txt
**/CMakeFiles
**/cmake_install.cmake
**/CTestTestfile.cmake
tags
bin/
Testing/

# Ignore auto-generated makefiles from CMake.
**/Makefile
^Makefile/

**/.DS_Store
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ before_install:
- echo $LC_ALL
before_script:
script:
- set -e
- rvm get head # Workaround 'shell_session_update: command not found'
- make test
- python setup.py build
- python setup.py test
# Test CMake build scripts.
- cmake . -Bbuild && cmake --build build --target run_tests
branches:
only:
- master
Expand Down
113 changes: 113 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Travis CI runs CMake 2.8.7 so we are pinned to that verison.
cmake_minimum_required(VERSION 2.8.7)
include(ExternalProject)

# User-configurable options.
option(BUILD_JSONNET "Build jsonnet command-line tool." ON)
option(BUILD_TESTS "Build and run jsonnet tests." ON)
set(GLOBAL_OUTPUT_PATH_SUFFIX "" CACHE STRING
"Output artifacts directory.")

project(jsonnet C CXX)

# Discourage in-source builds because they overwrite the hand-written Makefile.
# Use `cmake . -B<dir>` or the CMake GUI to do an out-of-source build.
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR} AND
${CMAKE_GENERATOR} MATCHES "Makefile")
message(WARNING "In-source builds with the a makefile generator overwrite the handwritten Makefile. Out-of-source builds are recommended for this project.")
endif()

# Disable CMake >3.0 warnings on Mac OS.
set(CMAKE_MACOSX_RPATH 1)

# Set output paths.
set(GLOBAL_OUTPUT_PATH "${PROJECT_BINARY_DIR}/${GLOBAL_OUTPUT_PATH_SUFFIX}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH})

# Include external googletest project. This runs a CMake sub-script
# (CMakeLists.txt.in) that downloads googletest source. It's then built as part
# of the jsonnet project. The conventional way of handling CMake dependencies is
# to use a find_package script, which finds and installs the library from
# known locations on the local machine. Downloading the library ourselves
# allows us to pin to a specific version and makes things easier for users
# who don't have package managers.
if (BUILD_TESTS)
enable_testing()

# Generate and download googletest project.
set(GOOGLETEST_DIR ${GLOBAL_OUTPUT_PATH}/googletest-download)
configure_file(CMakeLists.txt.in ${GOOGLETEST_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${GOOGLETEST_DIR}
)
if(result)
message(FATAL_ERROR "googletest download failed: ${result}")
endif()

# Build googletest.
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${GOOGLETEST_DIR})
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()

# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${GLOBAL_OUTPUT_PATH}/googletest-src
${GLOBAL_OUTPUT_PATH}/googletest-build)

# Include googletest headers.
include_directories("${gtest_SOURCE_DIR}/include")
endif()

# Compiler flags.
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR
${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
set(OPT "-O3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -pedantic -std=c99 -O3 ${OPT}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Woverloaded-virtual -pedantic -std=c++0x -fPIC ${OPT}")
else()
# TODO: Windows support.
message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} not supported")
endif()

# Look for libraries in global output path.
link_directories(${GLOBAL_OUTPUT_PATH})

# Targets

include_directories(
include
third_party/md5
core)

install(DIRECTORY include DESTINATION include)

add_subdirectory(stdlib)
add_subdirectory(third_party/md5)
add_subdirectory(core)
add_subdirectory(cmd)
add_subdirectory(test_suite)

if (BUILD_TESTS)
# Set JSONNET_CMD variable required for regression tests.
file(TO_NATIVE_PATH ${GLOBAL_OUTPUT_PATH}/jsonnet JSONNET_CMD)
set(CTEST_ENVIRONMENT
"JSONNET_CMD=${JSONNET_CMD}")

# s`run_tests` target builds and runs all tests. The cmake-generated `test`
# target runs tests without building them.
add_custom_target(run_tests COMMAND ${CMAKE_CTEST_COMMAND}
DEPENDS libjsonnet_test libjsonnet_test_file libjsonnet_test_snippet
jsonnet parser_test lexer_test
)

endif()
18 changes: 18 additions & 0 deletions CMakeLists.txt.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# CMake script run a generation-time. This must be separate from the main
# CMakeLists.txt file to allow downloading and building googletest at generation
# time.
cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${GLOBAL_OUTPUT_PATH}/googletest-src"
BINARY_DIR "${GLOBAL_OUTPUT_PATH}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
2 changes: 2 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ git_repository(
new_git_repository(
name = "com_google_googletest",
remote = "https://github.com/google/googletest.git",

# If updating googletest version, also update CMakeLists.txt.in.
tag = "release-1.8.0",
build_file = "gmock.BUILD",
)
Expand Down
9 changes: 9 additions & 0 deletions cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Jsonnet command-line tool.

if (BUILD_JSONNET OR BUILD_TESTS)
add_executable(jsonnet ${LIBJSONNET_SOURCE} jsonnet.cpp)
add_dependencies(jsonnet libjsonnet_static)
target_link_libraries(jsonnet libjsonnet_static)

install(TARGETS jsonnet DESTINATION bin)
endif()
83 changes: 83 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# libjsonnet

# Remember to update Bazel and Makefile builds when updating this list!
set(LIBJSONNET_HEADERS
ast.h
desugarer.h
formatter.h
lexer.h
../include/libjsonnet.h
parser.h
pass.h
state.h
static_analysis.h
static_error.h
std.jsonnet.h
string_utils.h
unicode.h
vm.h)

set(LIBJSONNET_SOURCE
desugarer.cpp
formatter.cpp
lexer.cpp
libjsonnet.cpp
parser.cpp
pass.cpp
static_analysis.cpp
string_utils.cpp
vm.cpp)

add_library(libjsonnet SHARED ${LIBJSONNET_HEADERS} ${LIBJSONNET_SOURCE})
add_dependencies(libjsonnet md5 stdlib)
target_link_libraries(libjsonnet md5)

# CMake prepends CMAKE_SHARED_LIBRARY_PREFIX to shared libraries, so without
# this step the output would be |liblibjsonnet|.
set_target_properties(libjsonnet PROPERTIES OUTPUT_NAME jsonnet)
install(TARGETS libjsonnet DESTINATION lib)

# Static library for jsonnet command-line tool.
add_library(libjsonnet_static STATIC ${LIBJSONNET_SOURCE})
add_dependencies(libjsonnet_static md5)
target_link_libraries(libjsonnet_static md5)
set_target_properties(libjsonnet_static PROPERTIES OUTPUT_NAME jsonnet)

# Tests

function(add_test_executable test_name)
if (EXISTS ${CMAKE_CURRENT_LIST_DIR}/${test_name}.cpp)
set(TEST_EXT cpp)
else()
set(TEST_EXT c)
endif()
add_executable(${test_name} ${test_name}.${TEST_EXT})

add_dependencies(${test_name} libjsonnet_static gtest_main)
target_link_libraries(
${test_name} gtest gtest_main libjsonnet_static)
endfunction()

if (BUILD_TESTS)
add_test_executable(lexer_test)
add_test(lexer_test ${GLOBAL_OUTPUT_PATH}/lexer_test)

add_test_executable(parser_test)
add_test(parser_test ${GLOBAL_OUTPUT_PATH}/parser_test)

add_test_executable(libjsonnet_test)
add_test(libjsonnet_test ${GLOBAL_OUTPUT_PATH}/libjsonnet_test)

add_test_executable(libjsonnet_test_file)
add_test(libjsonnet_test_file
${GLOBAL_OUTPUT_PATH}/libjsonnet_test_file
${CMAKE_SOURCE_DIR}/test_suite/object.jsonnet)

set(TEST_SNIPPET "std.assertEqual(({ x: 1, y: self.x } { x: 2 }).y, 2)")
add_test_executable(libjsonnet_test_snippet)
add_test(libjsonnet_test_snippet
${GLOBAL_OUTPUT_PATH}/libjsonnet_test_snippet ${TEST_SNIPPET})

add_test(jsonnet_test_snippet
${GLOBAL_OUTPUT_PATH}/jsonnet -e ${TEST_SNIPPET})
endif()
5 changes: 5 additions & 0 deletions include/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
set(LIB_HEADER
${LIB_HEADER}
${CMAKE_CURRENT_SOURCE_DIR}/libjsonnet.h
${CMAKE_CURRENT_SOURCE_DIR}/libjsonnet++.h
PARENT_SCOPE)
15 changes: 15 additions & 0 deletions stdlib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Utility to convert file to a list of integeters.

add_executable(to_c_array to_c_array.cpp)

# Custom command that will only build stdlib when it changes.
add_custom_command(
OUTPUT ${PROJECT_SOURCE_DIR}/core/std.jsonnet.h
COMMAND ${GLOBAL_OUTPUT_PATH}/to_c_array
${PROJECT_SOURCE_DIR}/stdlib/std.jsonnet
${PROJECT_SOURCE_DIR}/core/std.jsonnet.h
DEPENDS to_c_array std.jsonnet)

# Standard library build target that libjsonnet can depend on.
add_custom_target(stdlib ALL
DEPENDS ${PROJECT_SOURCE_DIR}/core/std.jsonnet.h)
41 changes: 41 additions & 0 deletions stdlib/to_c_array.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Converts stdin string to a comma-separated list of byte values and prints the
// result. Used for transforming the standard library into a C array.
//
// Usage:
// to_c_array <infile> <outfile>
#include <fstream>
#include <iostream>
#include <iterator>

int main(int argc, char *argv[]) {
if (argc < 3) {
std::cerr << "usage: to_c_array <infile> <outfile>\n";
return 1;
}

std::ifstream in_file(argv[1]);
if (!in_file.is_open()) {
std::cerr << "Can't open input file.";
return 1;
}

std::ofstream out_file(argv[2]);
if (!out_file.is_open()) {
std::cerr << "Can't open output file.";
return 1;
}

char c;
bool first_character = true;
while (in_file.get(c)) {
if (first_character) {
first_character = false;
} else {
out_file << ",";
}
// Write byte value of c to stdout.
out_file << (int)c;
}

return 0;
}
11 changes: 11 additions & 0 deletions test_suite/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
find_program(BASH bash)
if (BASH STREQUAL "BASH-NOTFOUND")
message(WARNING "Bash not found, can't run regression tests.")
else()
# Note: this test relies on the JSONNET_CMD environment variable, which
# is set in the root CMakeLists.txt.
add_test(
NAME regression_test
COMMAND ${BASH} ${CMAKE_CURRENT_SOURCE_DIR}/run_tests.sh
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
1 change: 1 addition & 0 deletions third_party/md5/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_library(md5 STATIC md5.cpp md5.h)

0 comments on commit 30f4739

Please sign in to comment.