Skip to content

CMake build with find_package support and automatic translation. #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CI
on: [ push, pull_request, workflow_dispatch ]
jobs:
test:
name: test ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [
windows-latest,
# macos-latest, # cppfront is currently broken on AppleClang
ubuntu-latest,
]
runs-on: ${{ matrix.os }}
steps:
# System set-up
- uses: actions/checkout@v2
- uses: ilammy/msvc-dev-cmd@v1
- uses: lukka/get-cmake@latest

- name: Install GCC 11
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get update
sudo apt-get install -y gcc-11 g++-11
echo "CC=gcc-11" >> $GITHUB_ENV
echo "CXX=g++-11" >> $GITHUB_ENV

# Main cppfront library
- name: Configure cppfront
run: cmake -G Ninja -S . -B build/cppfront -DCMAKE_BUILD_TYPE=Release
- name: Build cppfront
run: cmake --build build/cppfront --config Release
- name: Install cppfront locally
run: cmake --install build/cppfront --config Release --prefix _local

# Regression tests
- name: Configure regression tests
run: >
cmake -G Ninja -S regression-tests -B build/regression-tests -DCMAKE_BUILD_TYPE=Release
"-DCMAKE_PREFIX_PATH=${{github.workspace}}/_local"
- name: Build regression tests
run: cmake --build build/regression-tests --config Release
- name: Run regression tests
run: ctest --output-on-failure -C Release -j 2
working-directory: build/regression-tests
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

*.vcxproj
*.filters
*.recipe
Expand All @@ -22,3 +21,6 @@
*.vsidx
*.lock
.editorconfig
build/
*local/
.idea/
171 changes: 171 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
cmake_minimum_required(VERSION 3.23)
project(
cppfront
LANGUAGES CXX
VERSION 0.1.0
)

##
# Project options

include(CMakeDependentOption)

cmake_dependent_option(
CPPFRONT_INSTALL_RULES "Include install rules for cppfront" "${PROJECT_IS_TOP_LEVEL}"
"NOT CMAKE_SKIP_INSTALL_RULES" OFF
)

cmake_dependent_option(
CPPFRONT_NO_SYSTEM "Do not mark cpp2 runtime headers as SYSTEM" OFF
"NOT PROJECT_IS_TOP_LEVEL" ON
)
mark_as_advanced(CPPFRONT_NO_SYSTEM)

##
# Compute option-derived constants

if (CPPFRONT_NO_SYSTEM)
set(_cppfront_SYSTEM "")
else ()
set(_cppfront_SYSTEM SYSTEM)
endif ()

##
# Target definition for cppfront executable

add_executable(cppfront_cppfront source/cppfront.cpp)
add_executable(cppfront::cppfront ALIAS cppfront_cppfront)
set_target_properties(
cppfront_cppfront
PROPERTIES
OUTPUT_NAME cppfront
EXPORT_NAME cppfront
)

target_compile_features(cppfront_cppfront PRIVATE cxx_std_20)
target_sources(
cppfront_cppfront
PRIVATE
FILE_SET HEADERS
BASE_DIRS source
FILES source/common.h source/lex.h source/load.h source/parse.h source/sema.h
)

##
# Target definition for cpp2util runtime library

add_library(cppfront_cpp2util INTERFACE)
add_library(cppfront::cpp2util ALIAS cppfront_cpp2util)
set_target_properties(cppfront_cpp2util PROPERTIES EXPORT_NAME cpp2util)

target_compile_features(cppfront_cpp2util INTERFACE cxx_std_20)
target_include_directories(
cppfront_cpp2util ${_cppfront_SYSTEM} INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
)
target_sources(
cppfront_cpp2util
INTERFACE
FILE_SET HEADERS
BASE_DIRS include
FILES include/cpp2util.h
)

##
# Enable cpp2 autodetection for add_subdirectory users

if (NOT PROJECT_IS_TOP_LEVEL)
get_directory_property(_CPPFRONT_MAGIC_DIR PARENT_DIRECTORY)
include(cmake/CppfrontHelpers.cmake)
endif ()

##
# Installation and CMake packaging logic

if (CPPFRONT_INSTALL_RULES)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

set(CPPFRONT_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/cmake/cppfront"
CACHE STRING "Install location for cppfront's platform-independent CMake files")

set(CPPFRONT_INSTALL_EXE_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/cppfront-exe"
CACHE STRING "Install location for cppfront's platform-specific CMake files")

mark_as_advanced(CPPFRONT_INSTALL_CMAKEDIR CPPFRONT_INSTALL_EXE_CMAKEDIR)

##
# Install rules for cppfront executable (which is naturally platform
# dependent)

install(
TARGETS cppfront_cppfront
EXPORT cppfront-exe-targets
COMPONENT cppfront
)

install(
EXPORT cppfront-exe-targets
DESTINATION "${CPPFRONT_INSTALL_EXE_CMAKEDIR}"
NAMESPACE cppfront::
COMPONENT cppfront
)

configure_package_config_file(
cmake/cppfront-exe-config.cmake.in
cmake/cppfront-exe-config.cmake
INSTALL_DESTINATION "${CPPFRONT_INSTALL_EXE_CMAKEDIR}"
PATH_VARS CMAKE_INSTALL_BINDIR
)

write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-exe-config-version.cmake"
COMPATIBILITY ExactVersion
ARCH_INDEPENDENT # to allow finding host executables in cross-builds
)

install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-exe-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-exe-config-version.cmake"
DESTINATION "${CPPFRONT_INSTALL_EXE_CMAKEDIR}"
COMPONENT cppfront
)

##
# Install rules for cppfront CMake helpers and runtime headers,
# which are platform-independent

install(
TARGETS cppfront_cpp2util
EXPORT cppfront-targets
FILE_SET HEADERS
COMPONENT cppfront
)
install(
EXPORT cppfront-targets
DESTINATION "${CPPFRONT_INSTALL_CMAKEDIR}"
NAMESPACE cppfront::
COMPONENT cppfront
)

configure_package_config_file(
cmake/cppfront-config.cmake.in
cmake/cppfront-config.cmake
INSTALL_DESTINATION "${CPPFRONT_INSTALL_CMAKEDIR}"
)

write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-config-version.cmake"
COMPATIBILITY ExactVersion
ARCH_INDEPENDENT
)

install(
FILES
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-config.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cppfront-config-version.cmake"
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/CppfrontHelpers.cmake"
DESTINATION "${CPPFRONT_INSTALL_CMAKEDIR}"
COMPONENT cppfront
)
endif ()
93 changes: 93 additions & 0 deletions cmake/CppfrontHelpers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
function(_cppfront_unique_name base hash outvar)
string(LENGTH "${hash}" len)
foreach (i RANGE 0 "${len}")
string(SUBSTRING "${hash}" 0 "${i}" uniq)
if (uniq)
set(name "${base}-${uniq}")
else ()
set(name "${base}")
endif ()
get_property(name_used GLOBAL PROPERTY "cppfront/names/${name}" SET)
if (NOT name_used)
set("${outvar}" "${name}" PARENT_SCOPE)
set_property(GLOBAL PROPERTY "cppfront/names/${name}" 1)
return()
endif ()
endforeach ()
# This should be impossible, unless caching in _cppfront_generate_source
# is broken.
message(FATAL_ERROR "Could not compute a unique name using ${base} and ${hash}")
endfunction()

function(_cppfront_generate_source src out)
# TODO: there's probably something nicer/more user-friendly to be
# done here, but it's maybe not worth thinking about until cppfront's
# command line improves.

file(REAL_PATH "${src}" src)
string(SHA256 src_hash "${src}")

get_property(out_file GLOBAL PROPERTY "cppfront/out_file/${src_hash}")
if (out_file)
set("${out}" "${out_file}" PARENT_SCOPE)
return()
endif ()

cmake_path(GET src STEM original_stem)
_cppfront_unique_name("${original_stem}" "${src_hash}" basename)

# assume no SHA256 collisions
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/_cppfront/")
set(src_file "${CMAKE_BINARY_DIR}/_cppfront/${basename}.cpp2")
set(out_file "${CMAKE_BINARY_DIR}/_cppfront/${basename}.cpp")

add_custom_command(
OUTPUT "${out_file}"
COMMAND "${CMAKE_COMMAND}" -E copy "${src}" "${src_file}"
COMMAND cppfront::cppfront "${basename}.cpp2" ${CPPFRONT_FLAGS}
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/_cppfront"
DEPENDS "${src}" cppfront::cppfront
VERBATIM
)

set_property(GLOBAL PROPERTY "cppfront/out_file/${src_hash}" "${out_file}")
set("${out}" "${out_file}" PARENT_SCOPE)
endfunction()

function(cppfront_generate_cpp srcs)
set(cpp2srcs "")
foreach (src IN LISTS ARGN)
_cppfront_generate_source("${src}" cpp2)
list(APPEND cpp2srcs "${cpp2}")
endforeach ()
set("${srcs}" "${cpp2srcs}" PARENT_SCOPE)
endfunction()

function(cppfront_enable)
cmake_parse_arguments(PARSE_ARGV 0 ARG "" "" "TARGETS")

foreach (tgt IN LISTS ARG_TARGETS)
get_property(sources TARGET "${tgt}" PROPERTY SOURCES)
list(FILTER sources INCLUDE REGEX "\\.cpp2$")

if (sources)
target_link_libraries("${tgt}" PRIVATE cppfront::cpp2util)
cppfront_generate_cpp(cpp1sources ${sources})
target_sources("${tgt}" PRIVATE ${cpp1sources})
endif ()
endforeach ()
endfunction()

if (NOT CPPFRONT_NO_MAGIC)
function(_cppfront_enable_dir)
get_property(targets DIRECTORY . PROPERTY BUILDSYSTEM_TARGETS)
cppfront_enable(TARGETS ${targets})
endfunction()

if (NOT _CPPFRONT_MAGIC_DIR)
set(_CPPFRONT_MAGIC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
endif ()

message(VERBOSE "Enabling cppfront for all targets in ${_CPPFRONT_MAGIC_DIR}")
cmake_language(DEFER DIRECTORY "${_CPPFRONT_MAGIC_DIR}" CALL _cppfront_enable_dir)
endif ()
10 changes: 10 additions & 0 deletions cmake/cppfront-config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.23)
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)
find_dependency(cppfront-exe)

include("${CMAKE_CURRENT_LIST_DIR}/cppfront-targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/CppfrontHelpers.cmake")

check_required_components(cppfront)
7 changes: 7 additions & 0 deletions cmake/cppfront-exe-config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.23)
@PACKAGE_INIT@

include("${CMAKE_CURRENT_LIST_DIR}/cppfront-exe-targets.cmake")

set_and_check(CPPFRONT_EXECUTABLE "@PACKAGE_CMAKE_INSTALL_BINDIR@/cppfront@CMAKE_EXECUTABLE_SUFFIX@")
check_required_components(cppfront-exe)
8 changes: 8 additions & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.23)
project(cpp2-example)

find_package(cppfront REQUIRED)
# This works, too:
# add_subdirectory(../ cppfront)

add_executable(main main.cpp2)
21 changes: 21 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# CPPFront CMake example

This is a simple demo of this fork's CMake integration. To build it,
first build cppfront. From this directory, run:

```
$ cmake -G Ninja -S .. -B build/cppfront -DCMAKE_BUILD_TYPE=Release
$ cmake --build build/cppfront
$ cmake --install build/cppfront --prefix _local
```

Now we'll configure this project to use the locally installed cppfront:

```
$ cmake -G Ninja -S . -B build/example -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=$PWD/_local
$ cmake --build build/example
$ ./build/example/main
$ cat xyxxy
Hello Fred with UFCS!
```
8 changes: 8 additions & 0 deletions example/main.cpp2
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "A better C than C" ... ?
//
main: () -> int = {
s: std::string = "Fred";
myfile := fopen("xyzzy", "w");
myfile.fprintf( "Hello %s with UFCS!\n", s.c_str() );
myfile.fclose();
}
Loading