mirror of
https://github.com/build-cpp/cmkr
synced 2026-06-08 13:22:55 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9622334bf1 | |||
| 6ad29ef624 | |||
| 718c2c7527 | |||
| 8ea0c7396c | |||
| 34c12379c2 | |||
| 6395267e4b | |||
| 6d9b40bd15 | |||
| c4673d59e2 | |||
| 220dec6bbb | |||
| 4efa74f91f | |||
| fbd07c47bc | |||
| 17d4dc0555 | |||
| a6dfb2272d | |||
| 7fdafa4189 | |||
| 7cdf36f317 | |||
| bf14f069a7 | |||
| 3b4bc919a1 | |||
| c0bdbeb9d8 | |||
| 42725d0339 | |||
| 8f393d967d | |||
| 1cf9240579 | |||
| 7b7b260379 | |||
| 747bc8be00 | |||
| 00121c261f | |||
| 326647011b | |||
| 598c9b9ba8 | |||
| d62dff29b1 | |||
| 783f343e40 | |||
| 5312b44860 | |||
| 6162e86329 | |||
| 5a20c0fe42 | |||
| b5752b7c2b | |||
| 5c942599a5 |
@@ -0,0 +1 @@
|
||||
CMakeLists.txt linguist-generated
|
||||
@@ -2,18 +2,33 @@ name: CMake
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Skip building pull requests from the same repository
|
||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != '${{ github.repository }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [windows-latest, macos-10.15, ubuntu-18.04]
|
||||
|
||||
os: [windows-2016, windows-2019, macos-10.15, ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
run: cmake -S. -Bbin -DCMAKE_BUILD_TYPE=$BUILD_TYPE && cmake --build bin --parallel --config $BUILD_TYPE
|
||||
run: |
|
||||
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} "-DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/install"
|
||||
cmake --build build --config ${{ env.BUILD_TYPE }} --parallel
|
||||
cmake --install build --config ${{ env.BUILD_TYPE }}
|
||||
|
||||
- name: Check if cmkr was run
|
||||
run: |
|
||||
./install/bin/cmkr gen
|
||||
git diff --exit-code
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd build/tests
|
||||
ctest -C ${{ env.BUILD_TYPE }}
|
||||
|
||||
+4
-3
@@ -5,6 +5,7 @@ compile_commands.json
|
||||
temp.*
|
||||
.vs
|
||||
.cache
|
||||
build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
build*/
|
||||
.idea/
|
||||
cmake-build*/
|
||||
CMakeLists.txt.user
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.1.3 - 2020-11-27
|
||||
- Support building with C++11.
|
||||
- @mrexodia implemented CMake integration and bootstrapping.
|
||||
- Add dependency on ghc_filesystem and mpark_variant which are fetched automatically using FetchContent.
|
||||
|
||||
## 0.1.2 - 2020-11-20
|
||||
- Add support for target properties.
|
||||
- Add installs.
|
||||
- Require cmake >= 3.15.
|
||||
- Support settings and caching settings.
|
||||
- Support config when running cmkr build.
|
||||
- Add prompt prior to CMakeLists.txt generation if already existent in the current dir.
|
||||
|
||||
## 0.1.1 - 2020-11-19
|
||||
- Add support for globbing.
|
||||
- Add support for find_package components.
|
||||
- Add options.
|
||||
- Support aliases.
|
||||
- Support interface libs (header-only libs).
|
||||
- Support testing.
|
||||
Generated
+63
-53
@@ -1,39 +1,38 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
# This file is automatically generated from cmake.toml - DO NOT EDIT
|
||||
# See https://github.com/build-cpp/cmkr for more information
|
||||
|
||||
cmake_minimum_required(VERSION 2.8...3.8)
|
||||
|
||||
# Regenerate CMakeLists.txt file when necessary
|
||||
# Regenerate CMakeLists.txt automatically in the root project
|
||||
set(CMKR_ROOT_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CMKR_ROOT_PROJECT ON)
|
||||
|
||||
# Bootstrap cmkr
|
||||
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||
if(CMKR_INCLUDE_RESULT)
|
||||
cmkr()
|
||||
endif()
|
||||
|
||||
# Enable folder support
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
endif()
|
||||
|
||||
# Enable folder support
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# Hack to hide a warning during cmkr bootstrapping on Windows
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
|
||||
project(cmkr
|
||||
LANGUAGES
|
||||
CXX
|
||||
VERSION
|
||||
0.1.3
|
||||
0.1.4
|
||||
DESCRIPTION
|
||||
"CMakeLists generator from TOML"
|
||||
)
|
||||
|
||||
# third_party
|
||||
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||
if(CMAKE_FOLDER)
|
||||
set(CMAKE_FOLDER "${CMAKE_FOLDER}/third_party")
|
||||
@@ -43,53 +42,51 @@ endif()
|
||||
add_subdirectory(third_party)
|
||||
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||
|
||||
# tests
|
||||
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
|
||||
if(CMAKE_FOLDER)
|
||||
set(CMAKE_FOLDER "${CMAKE_FOLDER}/tests")
|
||||
else()
|
||||
set(CMAKE_FOLDER tests)
|
||||
endif()
|
||||
add_subdirectory(tests)
|
||||
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
|
||||
|
||||
# Target cmkrlib
|
||||
set(cmkrlib_SOURCES
|
||||
"src/cmkrlib/args.cpp"
|
||||
"src/cmkrlib/build.cpp"
|
||||
"src/cmkrlib/cmake.cpp"
|
||||
"src/cmkrlib/error.cpp"
|
||||
"src/cmkrlib/gen.cpp"
|
||||
"src/cmkrlib/help.cpp"
|
||||
"src/cmkrlib/cmake.hpp"
|
||||
"src/cmkrlib/enum_helper.hpp"
|
||||
"src/cmkrlib/fs.hpp"
|
||||
"include/args.h"
|
||||
"include/build.h"
|
||||
"include/error.h"
|
||||
"include/gen.h"
|
||||
"include/help.h"
|
||||
"include/literals.h"
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
add_library(cmkrlib STATIC ${cmkrlib_SOURCES})
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkrlib_SOURCES})
|
||||
|
||||
target_compile_features(cmkrlib PUBLIC
|
||||
cxx_std_11
|
||||
)
|
||||
|
||||
target_include_directories(cmkrlib PUBLIC
|
||||
include
|
||||
)
|
||||
|
||||
target_link_libraries(cmkrlib PUBLIC
|
||||
toml11
|
||||
ghc_filesystem
|
||||
mpark_variant
|
||||
ordered_map
|
||||
)
|
||||
|
||||
# Target cmkr
|
||||
set(cmkr_SOURCES
|
||||
set(CMKR_TARGET cmkr)
|
||||
set(cmkr_SOURCES "")
|
||||
|
||||
list(APPEND cmkr_SOURCES
|
||||
"src/args.cpp"
|
||||
"src/build.cpp"
|
||||
"src/cmake.cpp"
|
||||
"src/error.cpp"
|
||||
"src/gen.cpp"
|
||||
"src/help.cpp"
|
||||
"src/main.cpp"
|
||||
"include/args.hpp"
|
||||
"include/build.hpp"
|
||||
"include/cmake.hpp"
|
||||
"include/enum_helper.hpp"
|
||||
"include/error.hpp"
|
||||
"include/fs.hpp"
|
||||
"include/gen.hpp"
|
||||
"include/help.hpp"
|
||||
"include/literals.hpp"
|
||||
)
|
||||
|
||||
list(APPEND cmkr_SOURCES
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
add_executable(cmkr ${cmkr_SOURCES})
|
||||
set(CMKR_SOURCES ${cmkr_SOURCES})
|
||||
add_executable(cmkr)
|
||||
|
||||
if(cmkr_SOURCES)
|
||||
target_sources(cmkr PRIVATE ${cmkr_SOURCES})
|
||||
endif()
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
@@ -98,10 +95,23 @@ endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${cmkr_SOURCES})
|
||||
|
||||
target_link_libraries(cmkr PRIVATE
|
||||
cmkrlib
|
||||
target_compile_features(cmkr PRIVATE
|
||||
cxx_std_11
|
||||
)
|
||||
|
||||
target_include_directories(cmkr PRIVATE
|
||||
include
|
||||
)
|
||||
|
||||
target_link_libraries(cmkr PRIVATE
|
||||
toml11
|
||||
ghc_filesystem
|
||||
mpark_variant
|
||||
ordered_map
|
||||
)
|
||||
|
||||
unset(CMKR_TARGET)
|
||||
unset(CMKR_SOURCES)
|
||||
install(
|
||||
TARGETS
|
||||
cmkr
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
# cmkr
|
||||
|
||||
cmkr, pronounced "cmaker", is A CMakeLists.txt generator from TOML.
|
||||
cmkr, pronounced "cmaker", is a modern build system based on CMake and TOML. It was originally created by [Mohammed Alyousef](https://github.com/MoAlyousef).
|
||||
|
||||
**NOTE**: The documentation is currently a work-in-progress due to breaking changes since `0.1.4`. For examples you can check the [cmkr GitHub topic](https://github.com/topics/cmkr) and the [tests](https://github.com/build-cpp/cmkr/tree/main/tests).
|
||||
|
||||
## Building
|
||||
cmkr requires a C++11 compiler, cmake >= 3.15.
|
||||
```
|
||||
git clone https://github.com/moalyousef/cmkr
|
||||
cd cmkr
|
||||
cmake -Bbin
|
||||
cmake --build bin --parallel
|
||||
```
|
||||
## Getting started
|
||||
|
||||
`cmkr` parses `cmake.toml` files and generates a modern, idomatic `CMakeLists.txt` for you. A basic hello world format with the minimum required fields:
|
||||
|
||||
## Usage
|
||||
cmkr parses cmake.toml files (using toml11 by Toru Niina) at the project directory. A basic hello world format with the minimum required fields:
|
||||
```toml
|
||||
[cmake]
|
||||
minimum = "3.15"
|
||||
@@ -22,104 +16,26 @@ minimum = "3.15"
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
|
||||
[[target]]
|
||||
name = "app"
|
||||
[target.app]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
```
|
||||
|
||||
This project's cmake.toml:
|
||||
```toml
|
||||
[cmake]
|
||||
minimum = "3.15"
|
||||
## Building
|
||||
|
||||
[project]
|
||||
name = "cmkr"
|
||||
version = "0.1.3"
|
||||
`cmkr` requires a C++11 compiler and CMake >= ~3.x (exact minimum version is not yet specified). C++11 was picked to allow the broadest possible set of compilers to bootstrap `cmkr`.
|
||||
|
||||
[fetch-content]
|
||||
toml11 = { git = "https://github.com/ToruNiina/toml11" }
|
||||
filesystem = { git = "https://github.com/gulrak/filesystem" }
|
||||
mpark_variant = { url = "https://github.com/mpark/variant/archive/v1.4.0.tar.gz" }
|
||||
|
||||
[[target]]
|
||||
name = "cmkrlib"
|
||||
type = "static"
|
||||
sources = ["src/cmake.cpp", "src/gen.cpp", "src/help.cpp", "src/build.cpp", "src/error.cpp"]
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
link-libraries = ["toml11::toml11", "ghc_filesystem"]
|
||||
|
||||
[[target]]
|
||||
name = "cmkr"
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp", "src/args.cpp"]
|
||||
link-libraries = ["cmkrlib"]
|
||||
|
||||
[[install]]
|
||||
targets = ["cmkr"]
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
```sh
|
||||
git clone https://github.com/moalyousef/cmkr
|
||||
cd cmkr
|
||||
cmake -Bbin
|
||||
cmake --build bin --parallel
|
||||
```
|
||||
|
||||
Currently supported fields:
|
||||
```toml
|
||||
[cmake] # required for top-level project
|
||||
minimum = "3.15" # required
|
||||
subdirs = [] # optional
|
||||
build-dir = "build" # optional
|
||||
cpp-flags = [] # optional
|
||||
c-flags = [] # optional
|
||||
link-flags = [] # optional
|
||||
generator = "Ninja" # optional, only valid when run using: cmkr build
|
||||
config = "Release" # optional, only valid when run using: cmkr build
|
||||
arguments = ["CMAKE_TOOLCHAIN_FILE=/path/to/toolchain"] # optional, valid when run using: cmkr build
|
||||
## Command line
|
||||
|
||||
[settings] # optional
|
||||
CMAKE_BUILD_TYPE = "Release"
|
||||
TOML_BUILD_TESTS = false # optional
|
||||
TOML_BUILD_DOCS = { value = false, comment = "builds dependency docs", cache = true, force = true } # optional
|
||||
OLD_VERSION = "0.1.1" # optional
|
||||
The `cmkr` executable can be run from the command-line:
|
||||
|
||||
[project] # required per project
|
||||
name = "app" # required
|
||||
version = "0.1.0" # required
|
||||
|
||||
[find-package] # optional, runs find_package, use "*" to ignore version
|
||||
Boost = { version = "1.74.0", required = false, components = ["system"] } # optional
|
||||
spdlog = "*"
|
||||
|
||||
[fetch-content] # optional, runs fetchContent
|
||||
toml11 = { git = "https://github.com/ToruNiina/toml11", tag = "v3.5.0" } # optional
|
||||
|
||||
[options] # optional
|
||||
APP_BUILD_STUFF = false # optional
|
||||
APP_OTHER_STUFF = { comment = "does other stuff", value = false } # optional
|
||||
|
||||
[[target]] # required, can define several binaries
|
||||
name = "app" # required
|
||||
type = "executable" # required (executable || library || shared || static || interface)
|
||||
sources = ["src/*.cpp"] # required, supports globbing
|
||||
include-directories = ["include"] # optional
|
||||
alias = "" # optional
|
||||
compile-features = [] # optional
|
||||
compile-definitions = [] # optional
|
||||
link-libraries = [] # optional
|
||||
properties = { PROPERTY1 = "property1", ... } # optional
|
||||
|
||||
[[test]] # optional, can define several
|
||||
name = "test1" # required
|
||||
command = "app" # required
|
||||
arguments = ["arg1", "arg2"] # optional
|
||||
|
||||
[[install]] # optional, can define several
|
||||
targets = ["app"] # optional
|
||||
files = ["include/*.h"] # optional
|
||||
dirs = [] # optional
|
||||
configs = [] # optional (Release|Debug...etc)
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/bin" # required
|
||||
```
|
||||
|
||||
The cmkr executable can be run from the command-line:
|
||||
```
|
||||
Usage: cmkr [arguments]
|
||||
arguments:
|
||||
@@ -131,28 +47,9 @@ arguments:
|
||||
help Show help.
|
||||
version Current cmkr version.
|
||||
```
|
||||
The build command invokes cmake and the default build-system on your platform (unless a generator is specified), it also accepts extra cmake build arguments:
|
||||
```
|
||||
|
||||
The build command invokes `cmake` and the default build-system on your platform (unless a generator is specified), it also accepts extra build arguments:
|
||||
|
||||
```sh
|
||||
cmkr build --config Release
|
||||
```
|
||||
|
||||
## Binary types
|
||||
|
||||
### executable
|
||||
Executable binary. Equivalent to [add_executable(name)](https://cmake.org/cmake/help/latest/command/add_executable.html).
|
||||
|
||||
### library
|
||||
Library, can be static or shared depending on the BUILD_SHARED_LIBS variable. Equivalent to [add_library()](https://cmake.org/cmake/help/latest/command/add_library.html).
|
||||
|
||||
### static
|
||||
Static library/archive. Equivalent to [add_library(name STATIC)](https://cmake.org/cmake/help/latest/command/add_library.html).
|
||||
|
||||
### shared
|
||||
Shared/dynamic library. Equivalent to [add_library(name SHARED)](https://cmake.org/cmake/help/latest/command/add_library.html).
|
||||
|
||||
### interface
|
||||
Header-only library. Equivalent to [add_library(name INTERFACE)](https://cmake.org/cmake/help/latest/command/add_library.html).
|
||||
|
||||
## Roadmap
|
||||
- Support more cmake fields.
|
||||
- Support conditional cmake args somehow!
|
||||
|
||||
+6
-16
@@ -1,30 +1,20 @@
|
||||
[cmake]
|
||||
minimum = "2.8...3.8"
|
||||
version = "2.8...3.8"
|
||||
|
||||
[project]
|
||||
cmake-before = """
|
||||
# Hack to hide a warning during cmkr bootstrapping on Windows
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
endif()
|
||||
"""
|
||||
name = "cmkr"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
description = "CMakeLists generator from TOML"
|
||||
languages = ["CXX"]
|
||||
subdirs = ["third_party"]
|
||||
subdirs = ["third_party", "tests"]
|
||||
|
||||
[target.cmkrlib]
|
||||
type = "static"
|
||||
sources = ["src/cmkrlib/*.cpp", "src/cmkrlib/*.hpp", "include/*.h"]
|
||||
[target.cmkr]
|
||||
type = "executable"
|
||||
sources = ["src/*.cpp", "include/*.hpp"]
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
link-libraries = ["toml11", "ghc_filesystem", "mpark_variant", "ordered_map"]
|
||||
|
||||
[target.cmkr]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["cmkrlib"]
|
||||
|
||||
[[install]]
|
||||
targets = ["cmkr"]
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/bin"
|
||||
|
||||
Generated
+32
-15
@@ -1,27 +1,44 @@
|
||||
# This file was generated automatically by cmkr.
|
||||
|
||||
# Regenerate CMakeLists.txt file when necessary
|
||||
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||
|
||||
if(CMKR_INCLUDE_RESULT)
|
||||
cmkr()
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
# Regenerate CMakeLists.txt automatically in the root project
|
||||
set(CMKR_ROOT_PROJECT OFF)
|
||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
set(CMKR_ROOT_PROJECT ON)
|
||||
|
||||
set(example_PROJECT_VERSION 0.1.0)
|
||||
project(example VERSION ${example_PROJECT_VERSION})
|
||||
# Bootstrap cmkr
|
||||
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
|
||||
if(CMKR_INCLUDE_RESULT)
|
||||
cmkr()
|
||||
endif()
|
||||
|
||||
set(EXAMPLE_SOURCES
|
||||
src/example.cpp
|
||||
# Enable folder support
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
endif()
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
project(example
|
||||
VERSION
|
||||
0.1.0
|
||||
)
|
||||
|
||||
# Target example
|
||||
set(example_SOURCES
|
||||
"src/example.cpp"
|
||||
cmake.toml
|
||||
)
|
||||
|
||||
add_executable(example ${EXAMPLE_SOURCES})
|
||||
|
||||
source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${EXAMPLE_SOURCES})
|
||||
add_executable(example ${example_SOURCES})
|
||||
|
||||
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
|
||||
if(NOT CMKR_VS_STARTUP_PROJECT)
|
||||
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT example)
|
||||
endif()
|
||||
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${example_SOURCES})
|
||||
|
||||
|
||||
+2
-3
@@ -1,13 +1,12 @@
|
||||
# This is a minimal example project used for testing the cmkr bootstrapping process
|
||||
|
||||
[cmake]
|
||||
minimum = "3.15"
|
||||
version = "3.15"
|
||||
|
||||
[project]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
|
||||
[[target]]
|
||||
name = "example"
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/example.cpp"]
|
||||
+13
-4
@@ -1,8 +1,10 @@
|
||||
include_guard()
|
||||
|
||||
# Change these defaults to point to your infrastructure if desired
|
||||
set(CMKR_REPO "https://github.com/MoAlyousef/cmkr" CACHE STRING "cmkr git repository")
|
||||
set(CMKR_TAG "archive_67ac9a43" CACHE STRING "cmkr git tag (this needs to be available forever)")
|
||||
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
|
||||
set(CMKR_TAG "archive_7cdf36f3" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
|
||||
|
||||
# Set these from the command line to customize for development/debugging purposes
|
||||
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
|
||||
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
|
||||
|
||||
@@ -45,7 +47,14 @@ else()
|
||||
endif()
|
||||
|
||||
# Use cached cmkr if found
|
||||
set(CMKR_CACHED_EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/_cmkr/bin/${CMKR_EXECUTABLE_NAME}")
|
||||
set(CMKR_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_${CMKR_TAG}")
|
||||
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
|
||||
|
||||
if(NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE AND CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
|
||||
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
|
||||
unset(CMKR_EXECUTABLE CACHE)
|
||||
endif()
|
||||
|
||||
if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}")
|
||||
message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
|
||||
elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE)
|
||||
@@ -55,7 +64,6 @@ else()
|
||||
message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
|
||||
|
||||
message(STATUS "[cmkr] Fetching cmkr...")
|
||||
set(CMKR_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
|
||||
if(EXISTS "${CMKR_DIRECTORY}")
|
||||
cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
|
||||
endif()
|
||||
@@ -70,6 +78,7 @@ else()
|
||||
)
|
||||
message(STATUS "[cmkr] Building cmkr...")
|
||||
cmkr_exec("${CMAKE_COMMAND}"
|
||||
--no-warn-unused-cli
|
||||
"${CMKR_DIRECTORY}"
|
||||
"-B${CMKR_DIRECTORY}/build"
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace cmkr {
|
||||
namespace args {
|
||||
|
||||
@@ -9,11 +8,4 @@ const char *handle_args(int argc, char **argv);
|
||||
} // namespace args
|
||||
} // namespace cmkr
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *cmkr_args_handle_args(int, char **);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace cmkr {
|
||||
namespace build {
|
||||
|
||||
@@ -13,15 +11,9 @@ int install();
|
||||
|
||||
} // namespace build
|
||||
} // namespace cmkr
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int cmkr_build_run(int argc, char **argv);
|
||||
|
||||
int cmkr_build_clean(void);
|
||||
int cmkr_build_clean();
|
||||
|
||||
int cmkr_build_install(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
int cmkr_build_install();
|
||||
@@ -21,16 +21,22 @@ struct Setting {
|
||||
struct Option {
|
||||
std::string name;
|
||||
std::string comment;
|
||||
bool val;
|
||||
bool val = false;
|
||||
};
|
||||
|
||||
struct Package {
|
||||
std::string name;
|
||||
std::string version;
|
||||
bool required = true;
|
||||
bool config = true;
|
||||
std::vector<std::string> components;
|
||||
};
|
||||
|
||||
struct Vcpkg {
|
||||
std::string version;
|
||||
std::vector<std::string> packages;
|
||||
};
|
||||
|
||||
enum TargetType {
|
||||
target_executable,
|
||||
target_library,
|
||||
@@ -40,34 +46,41 @@ enum TargetType {
|
||||
target_custom,
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using Condition = tsl::ordered_map<std::string, T>;
|
||||
|
||||
using ConditionVector = Condition<std::vector<std::string>>;
|
||||
|
||||
struct Target {
|
||||
std::string name;
|
||||
TargetType type;
|
||||
TargetType type = {};
|
||||
|
||||
// https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html#project-commands
|
||||
std::vector<std::string> compile_definitions;
|
||||
std::vector<std::string> compile_features;
|
||||
std::vector<std::string> compile_options;
|
||||
std::vector<std::string> include_directories;
|
||||
std::vector<std::string> link_directories;
|
||||
std::vector<std::string> link_libraries;
|
||||
std::vector<std::string> link_options;
|
||||
std::vector<std::string> precompile_headers;
|
||||
std::vector<std::string> sources;
|
||||
ConditionVector compile_definitions;
|
||||
ConditionVector compile_features;
|
||||
ConditionVector compile_options;
|
||||
ConditionVector include_directories;
|
||||
ConditionVector link_directories;
|
||||
ConditionVector link_libraries;
|
||||
ConditionVector link_options;
|
||||
ConditionVector precompile_headers;
|
||||
ConditionVector sources;
|
||||
|
||||
std::string alias;
|
||||
tsl::ordered_map<std::string, std::string> properties;
|
||||
|
||||
std::string cmake_before;
|
||||
std::string cmake_after;
|
||||
std::vector<std::string> include_before;
|
||||
std::vector<std::string> include_after;
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
};
|
||||
|
||||
struct Test {
|
||||
std::string name;
|
||||
std::string cmd;
|
||||
std::vector<std::string> args;
|
||||
std::vector<std::string> configurations;
|
||||
std::string working_directory;
|
||||
std::string command;
|
||||
std::vector<std::string> arguments;
|
||||
};
|
||||
|
||||
struct Install {
|
||||
@@ -79,12 +92,13 @@ struct Install {
|
||||
};
|
||||
|
||||
struct CMake {
|
||||
std::string cmake_version;
|
||||
std::string cmkr_include;
|
||||
// This is the CMake version required to use all cmkr versions.
|
||||
std::string cmake_version = "3.15";
|
||||
std::string cmkr_include = "cmkr.cmake";
|
||||
std::string build_dir = "build";
|
||||
std::string generator;
|
||||
std::string config;
|
||||
std::vector<std::string> subdirs;
|
||||
Condition<std::vector<std::string>> subdirs;
|
||||
std::vector<std::string> cppflags;
|
||||
std::vector<std::string> cflags;
|
||||
std::vector<std::string> linkflags;
|
||||
@@ -94,17 +108,20 @@ struct CMake {
|
||||
std::string project_version;
|
||||
std::string project_description;
|
||||
std::vector<std::string> project_languages;
|
||||
std::string cmake_before;
|
||||
std::string cmake_after;
|
||||
std::vector<std::string> include_before;
|
||||
std::vector<std::string> include_after;
|
||||
Condition<std::string> cmake_before;
|
||||
Condition<std::string> cmake_after;
|
||||
ConditionVector include_before;
|
||||
ConditionVector include_after;
|
||||
std::vector<Setting> settings;
|
||||
std::vector<Option> options;
|
||||
std::vector<Package> packages;
|
||||
Vcpkg vcpkg;
|
||||
tsl::ordered_map<std::string, std::map<std::string, std::string>> contents;
|
||||
std::vector<Target> targets;
|
||||
std::vector<Test> tests;
|
||||
std::vector<Install> installs;
|
||||
tsl::ordered_map<std::string, std::string> conditions;
|
||||
|
||||
CMake(const std::string &path, bool build);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
@@ -25,11 +24,4 @@ struct Status {
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *cmkr_error_status_string(int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace cmkr {
|
||||
namespace gen {
|
||||
|
||||
@@ -11,13 +10,6 @@ int generate_cmake(const char *path, bool root = true);
|
||||
} // namespace gen
|
||||
} // namespace cmkr
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int cmkr_gen_generate_project(const char *typ);
|
||||
|
||||
int cmkr_gen_generate_cmake(const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace cmkr {
|
||||
namespace help {
|
||||
|
||||
@@ -11,13 +10,6 @@ const char *message() noexcept;
|
||||
} // namespace help
|
||||
} // namespace cmkr
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const char *cmkr_help_version(void);
|
||||
|
||||
const char *cmkr_help_message(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
static const char *hello_world = R"lit(
|
||||
static const char *hello_world = &R"lit(
|
||||
#include <iostream>
|
||||
|
||||
int %s() {
|
||||
std::cout << "Hello World!\n";
|
||||
return 0;
|
||||
}
|
||||
)lit" + 1; // skip initial newline
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *cmake_toml = R"lit(
|
||||
static const char *cmake_toml = &R"lit(
|
||||
[cmake]
|
||||
minimum = "3.15"
|
||||
version = "3.15"
|
||||
# subdirs = []
|
||||
# build-dir = ""
|
||||
# cpp-flags = []
|
||||
@@ -30,8 +30,7 @@ version = "0.1.0"
|
||||
|
||||
# [options]
|
||||
|
||||
[[target]]
|
||||
name = "%s"
|
||||
[target.%s]
|
||||
type = "%s"
|
||||
sources = ["src/*.cpp"]
|
||||
include-directories = ["include"]
|
||||
@@ -43,4 +42,4 @@ include-directories = ["include"]
|
||||
[[install]]
|
||||
%s = ["%s"]
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/%s"
|
||||
)lit" + 1; // skip initial newline
|
||||
)lit"[1]; // skip initial newline
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "args.h"
|
||||
#include "build.h"
|
||||
#include "gen.h"
|
||||
#include "help.h"
|
||||
#include "args.hpp"
|
||||
#include "build.hpp"
|
||||
#include "gen.hpp"
|
||||
#include "help.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <exception>
|
||||
@@ -21,8 +21,7 @@ const char *handle_args(int argc, char **argv) {
|
||||
throw std::runtime_error(cmkr::help::message());
|
||||
std::string main_arg = args[1];
|
||||
if (main_arg == "gen") {
|
||||
auto current_path = fs::current_path();
|
||||
auto ret = cmkr::gen::generate_cmake(current_path.string().c_str());
|
||||
auto ret = cmkr::gen::generate_cmake(fs::current_path().string().c_str());
|
||||
if (ret)
|
||||
return "CMake generation error!";
|
||||
return "CMake generation successful!";
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "build.h"
|
||||
#include "build.hpp"
|
||||
#include "cmake.hpp"
|
||||
#include "error.h"
|
||||
#include "gen.h"
|
||||
#include "error.hpp"
|
||||
#include "gen.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <sstream>
|
||||
@@ -16,15 +16,14 @@ namespace build {
|
||||
int run(int argc, char **argv) {
|
||||
cmake::CMake cmake(".", true);
|
||||
if (argc > 2) {
|
||||
for (size_t i = 2; i < argc; ++i) {
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
cmake.build_args.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
std::stringstream ss;
|
||||
|
||||
if (!fs::exists("CMakeLists.txt"))
|
||||
if (gen::generate_cmake("."))
|
||||
throw std::runtime_error("CMake generation failure!");
|
||||
if (gen::generate_cmake(fs::current_path().string().c_str()))
|
||||
throw std::runtime_error("CMake generation failure!");
|
||||
|
||||
ss << "cmake -S. -B" << cmake.build_dir << " ";
|
||||
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
#include "cmake.hpp"
|
||||
|
||||
#include "enum_helper.hpp"
|
||||
#include "fs.hpp"
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
|
||||
template <>
|
||||
const char *enumStrings<cmkr::cmake::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom"};
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
using TomlBasicValue = toml::basic_value<toml::preserve_comments, tsl::ordered_map, std::vector>;
|
||||
|
||||
template <typename EnumType>
|
||||
static EnumType to_enum(const std::string &str, const std::string &help_name) {
|
||||
EnumType value;
|
||||
try {
|
||||
std::stringstream ss(str);
|
||||
ss >> enumFromString(value);
|
||||
} catch (std::invalid_argument &) {
|
||||
std::string supported;
|
||||
for (const auto &s : enumStrings<EnumType>::data) {
|
||||
if (!supported.empty()) {
|
||||
supported += ", ";
|
||||
}
|
||||
supported += s;
|
||||
}
|
||||
throw std::runtime_error("Unknown " + help_name + "'" + str + "'! Supported types are: " + supported);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination);
|
||||
|
||||
template <typename T>
|
||||
static void get_optional(const TomlBasicValue &v, const toml::key &ky, Condition<T> &destination) {
|
||||
// TODO: this algorithm in O(n) over the amount of keys, kinda bad
|
||||
const auto &table = v.as_table();
|
||||
for (const auto &itr : table) {
|
||||
const auto &key = itr.first;
|
||||
const auto &value = itr.second;
|
||||
if (value.is_table()) {
|
||||
if (value.contains(ky)) {
|
||||
destination[key] = toml::find<T>(value, ky);
|
||||
}
|
||||
} else if (key == ky) {
|
||||
destination[""] = toml::find<T>(v, ky);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void get_optional(const TomlBasicValue &v, const toml::key &ky, T &destination) {
|
||||
if (v.contains(ky)) {
|
||||
destination = toml::find<T>(v, ky);
|
||||
}
|
||||
}
|
||||
|
||||
CMake::CMake(const std::string &path, bool build) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml was found!");
|
||||
}
|
||||
const auto toml = toml::parse<toml::preserve_comments, tsl::ordered_map, std::vector>((fs::path(path) / "cmake.toml").string());
|
||||
if (build) {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
|
||||
if (cmake.contains("bin-dir")) {
|
||||
throw std::runtime_error("bin-dir has been renamed to build-dir");
|
||||
}
|
||||
|
||||
get_optional(cmake, "build-dir", build_dir);
|
||||
get_optional(cmake, "generator", generator);
|
||||
get_optional(cmake, "config", config);
|
||||
get_optional(cmake, "arguments", gen_args);
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
cmake_version = toml::find(cmake, "version").as_string();
|
||||
get_optional(cmake, "cmkr-include", cmkr_include);
|
||||
get_optional(cmake, "cpp-flags", cppflags);
|
||||
get_optional(cmake, "c-flags", cflags);
|
||||
get_optional(cmake, "link-flags", linkflags);
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
const auto &project = toml::find(toml, "project");
|
||||
project_name = toml::find(project, "name").as_string();
|
||||
get_optional(project, "version", project_version);
|
||||
get_optional(project, "description", project_description);
|
||||
get_optional(project, "languages", project_languages);
|
||||
get_optional(project, "cmake-before", cmake_before);
|
||||
get_optional(project, "cmake-after", cmake_after);
|
||||
get_optional(project, "include-before", include_before);
|
||||
get_optional(project, "include-after", include_after);
|
||||
get_optional(project, "subdirs", subdirs);
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
using set_map = std::map<std::string, TomlBasicValue>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto &set : sets) {
|
||||
Setting s;
|
||||
s.name = set.first;
|
||||
if (set.second.is_boolean()) {
|
||||
s.val = set.second.as_boolean();
|
||||
} else if (set.second.is_string()) {
|
||||
s.val = set.second.as_string();
|
||||
} else {
|
||||
get_optional(set.second, "comment", s.comment);
|
||||
if (set.second.contains("value")) {
|
||||
auto v = toml::find(set.second, "value");
|
||||
if (v.is_boolean()) {
|
||||
s.val = v.as_boolean();
|
||||
} else {
|
||||
s.val = v.as_string();
|
||||
}
|
||||
}
|
||||
get_optional(set.second, "cache", s.cache);
|
||||
get_optional(set.second, "force", s.force);
|
||||
}
|
||||
settings.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("options")) {
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto &opt : opts) {
|
||||
Option o;
|
||||
o.name = opt.first;
|
||||
if (opt.second.is_boolean()) {
|
||||
o.val = opt.second.as_boolean();
|
||||
} else {
|
||||
get_optional(opt.second, "comment", o.comment);
|
||||
get_optional(opt.second, "value", o.val);
|
||||
}
|
||||
options.push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("find-package")) {
|
||||
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &pkg : pkgs) {
|
||||
Package p;
|
||||
p.name = pkg.first;
|
||||
if (pkg.second.is_string()) {
|
||||
p.version = pkg.second.as_string();
|
||||
} else {
|
||||
get_optional(pkg.second, "version", p.version);
|
||||
get_optional(pkg.second, "required", p.required);
|
||||
get_optional(pkg.second, "config", p.config);
|
||||
get_optional(pkg.second, "components", p.components);
|
||||
}
|
||||
packages.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to std::vector<Content> instead of this hacky thing?
|
||||
get_optional(toml, "fetch-content", contents);
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
|
||||
}
|
||||
|
||||
if (toml.contains("target")) {
|
||||
const auto &ts = toml::find(toml, "target").as_table();
|
||||
|
||||
for (const auto &itr : ts) {
|
||||
const auto &t = itr.second;
|
||||
Target target;
|
||||
target.name = itr.first;
|
||||
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
|
||||
|
||||
get_optional(t, "sources", target.sources);
|
||||
get_optional(t, "compile-definitions", target.compile_definitions);
|
||||
get_optional(t, "compile-features", target.compile_features);
|
||||
get_optional(t, "compile-options", target.compile_options);
|
||||
get_optional(t, "include-directories", target.include_directories);
|
||||
get_optional(t, "link-directories", target.link_directories);
|
||||
get_optional(t, "link-libraries", target.link_libraries);
|
||||
get_optional(t, "link-options", target.link_options);
|
||||
get_optional(t, "precompile-headers", target.precompile_headers);
|
||||
|
||||
if (t.contains("alias")) {
|
||||
target.alias = toml::find(t, "alias").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("properties")) {
|
||||
const auto &props = toml::find(t, "properties").as_table();
|
||||
for (const auto &propKv : props) {
|
||||
if (propKv.second.is_array()) {
|
||||
std::string property_list;
|
||||
for (const auto &list_val : propKv.second.as_array()) {
|
||||
if (!property_list.empty()) {
|
||||
property_list += ';';
|
||||
}
|
||||
property_list += list_val.as_string();
|
||||
}
|
||||
target.properties[propKv.first] = property_list;
|
||||
} else {
|
||||
target.properties[propKv.first] = propKv.second.as_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_optional(t, "cmake-before", target.cmake_before);
|
||||
get_optional(t, "cmake-after", target.cmake_after);
|
||||
get_optional(t, "include-before", target.include_before);
|
||||
get_optional(t, "include-after", target.include_after);
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("test")) {
|
||||
const auto &ts = toml::find(toml, "test").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Test test;
|
||||
test.name = toml::find(t, "name").as_string();
|
||||
get_optional(t, "configurations", test.configurations);
|
||||
get_optional(t, "working-directory", test.working_directory);
|
||||
test.command = toml::find(t, "command").as_string();
|
||||
get_optional(t, "arguments", test.arguments);
|
||||
tests.push_back(test);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("install")) {
|
||||
const auto &ts = toml::find(toml, "install").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Install inst;
|
||||
get_optional(t, "targets", inst.targets);
|
||||
get_optional(t, "files", inst.files);
|
||||
get_optional(t, "dirs", inst.dirs);
|
||||
get_optional(t, "configs", inst.configs);
|
||||
inst.destination = toml::find(t, "destination").as_string();
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("vcpkg")) {
|
||||
const auto &v = toml::find(toml, "vcpkg");
|
||||
vcpkg.version = toml::find(v, "version").as_string();
|
||||
vcpkg.packages = toml::find<decltype(vcpkg.packages)>(v, "packages");
|
||||
|
||||
// This allows the user to use a custom pmm version if desired
|
||||
if (contents.count("pmm") == 0) {
|
||||
contents["pmm"]["url"] = "https://github.com/vector-of-bool/pmm/archive/refs/tags/1.5.1.tar.gz";
|
||||
// Hack to not execute pmm's example CMakeLists.txt
|
||||
contents["pmm"]["SOURCE_SUBDIR"] = "pmm";
|
||||
}
|
||||
}
|
||||
|
||||
// Reasonable default conditions (you can override these if you desire)
|
||||
conditions["windows"] = R"cmake(WIN32)cmake";
|
||||
conditions["macos"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")cmake";
|
||||
conditions["unix"] = R"cmake(UNIX)cmake";
|
||||
conditions["bsd"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")cmake";
|
||||
conditions["linux"] = conditions["lunix"] = R"cmake("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")cmake";
|
||||
|
||||
if (toml.contains("conditions")) {
|
||||
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
|
||||
for (const auto &cond : conds) {
|
||||
conditions[cond.first] = cond.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
||||
@@ -1,224 +0,0 @@
|
||||
#include "cmake.hpp"
|
||||
|
||||
#include "enum_helper.hpp"
|
||||
#include "fs.hpp"
|
||||
#include <stdexcept>
|
||||
#include <toml.hpp>
|
||||
#include <tsl/ordered_map.h>
|
||||
|
||||
template <>
|
||||
const char *enumStrings<cmkr::cmake::TargetType>::data[] = {"executable", "library", "shared", "static", "interface", "custom"};
|
||||
|
||||
namespace cmkr {
|
||||
namespace cmake {
|
||||
|
||||
using TomlBasicValue = toml::basic_value<toml::preserve_comments, tsl::ordered_map, std::vector>;
|
||||
|
||||
template <typename EnumType>
|
||||
static EnumType to_enum(const std::string &str, const std::string &help_name) {
|
||||
EnumType value;
|
||||
try {
|
||||
std::stringstream ss(str);
|
||||
ss >> enumFromString(value);
|
||||
} catch (std::invalid_argument &) {
|
||||
std::string supported;
|
||||
for (const auto &s : enumStrings<EnumType>::data) {
|
||||
if (!supported.empty()) {
|
||||
supported += ", ";
|
||||
}
|
||||
supported += s;
|
||||
}
|
||||
throw std::runtime_error("Unknown " + help_name + "'" + str + "'! Supported types are: " + supported);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::vector<std::string> optional_array(const TomlBasicValue &v, const toml::key &ky) {
|
||||
return toml::find_or(v, ky, std::vector<std::string>());
|
||||
};
|
||||
|
||||
CMake::CMake(const std::string &path, bool build) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml was found!");
|
||||
}
|
||||
const auto toml = toml::parse<toml::preserve_comments, tsl::ordered_map, std::vector>((fs::path(path) / "cmake.toml").string());
|
||||
if (build) {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
|
||||
if (cmake.contains("bin-dir")) {
|
||||
throw std::runtime_error("bin-dir has been renamed to build-dir");
|
||||
}
|
||||
|
||||
build_dir = toml::find_or(cmake, "build-dir", "");
|
||||
generator = toml::find_or(cmake, "generator", "");
|
||||
config = toml::find_or(cmake, "config", "");
|
||||
gen_args = optional_array(cmake, "arguments");
|
||||
}
|
||||
} else {
|
||||
if (toml.contains("cmake")) {
|
||||
const auto &cmake = toml::find(toml, "cmake");
|
||||
cmake_version = toml::find(cmake, "minimum").as_string();
|
||||
cmkr_include = toml::find_or(cmake, "cmkr-include", "cmkr.cmake");
|
||||
cppflags = optional_array(cmake, "cpp-flags");
|
||||
cflags = optional_array(cmake, "c-flags");
|
||||
linkflags = optional_array(cmake, "link-flags");
|
||||
}
|
||||
|
||||
if (toml.contains("project")) {
|
||||
const auto &project = toml::find(toml, "project");
|
||||
project_name = toml::find(project, "name").as_string();
|
||||
project_version = toml::find_or(project, "version", "");
|
||||
project_description = toml::find_or(project, "description", "");
|
||||
project_languages = optional_array(project, "languages");
|
||||
cmake_before = toml::find_or(project, "cmake-before", "");
|
||||
cmake_after = toml::find_or(project, "cmake-after", "");
|
||||
include_before = optional_array(project, "include-before");
|
||||
include_after = optional_array(project, "include-after");
|
||||
subdirs = optional_array(project, "subdirs");
|
||||
}
|
||||
|
||||
if (toml.contains("settings")) {
|
||||
using set_map = std::map<std::string, TomlBasicValue>;
|
||||
const auto &sets = toml::find<set_map>(toml, "settings");
|
||||
for (const auto set : sets) {
|
||||
Setting s;
|
||||
s.name = set.first;
|
||||
if (set.second.is_boolean()) {
|
||||
s.val = set.second.as_boolean();
|
||||
} else if (set.second.is_string()) {
|
||||
s.val = set.second.as_string();
|
||||
} else {
|
||||
s.comment = toml::find_or(set.second, "comment", "");
|
||||
if (set.second.contains("value")) {
|
||||
auto v = toml::find(set.second, "value");
|
||||
if (v.is_boolean()) {
|
||||
s.val = v.as_boolean();
|
||||
} else {
|
||||
s.val = v.as_string();
|
||||
}
|
||||
}
|
||||
s.cache = toml::find_or(set.second, "cache", false);
|
||||
s.force = toml::find_or(set.second, "force", false);
|
||||
}
|
||||
settings.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("options")) {
|
||||
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &opts = toml::find<opts_map>(toml, "options");
|
||||
for (const auto opt : opts) {
|
||||
Option o;
|
||||
o.name = opt.first;
|
||||
if (opt.second.is_boolean()) {
|
||||
o.val = opt.second.as_boolean();
|
||||
} else {
|
||||
o.comment = toml::find_or(opt.second, "comment", "");
|
||||
o.val = toml::find_or(opt.second, "value", false);
|
||||
}
|
||||
options.push_back(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("find-package")) {
|
||||
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
|
||||
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
|
||||
for (const auto &pkg : pkgs) {
|
||||
Package p;
|
||||
p.name = pkg.first;
|
||||
if (pkg.second.is_string()) {
|
||||
p.version = pkg.second.as_string();
|
||||
} else {
|
||||
p.version = toml::find_or(pkg.second, "version", "");
|
||||
p.required = toml::find_or(pkg.second, "required", false);
|
||||
p.components = toml::find_or(pkg.second, "components", std::vector<std::string>());
|
||||
}
|
||||
packages.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor to std::vector<Content> instead of this hacky thing?
|
||||
contents = toml::find_or<decltype(contents)>(toml, "fetch-content", {});
|
||||
|
||||
if (toml.contains("bin")) {
|
||||
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
|
||||
}
|
||||
|
||||
if (toml.contains("target")) {
|
||||
const auto &ts = toml::find(toml, "target").as_table();
|
||||
|
||||
for (const auto &itr : ts) {
|
||||
const auto &t = itr.second;
|
||||
Target target;
|
||||
target.name = itr.first;
|
||||
target.type = to_enum<TargetType>(toml::find(t, "type").as_string(), "target type");
|
||||
|
||||
target.sources = optional_array(t, "sources");
|
||||
|
||||
#define renamed(from, to) \
|
||||
if (t.contains(from)) { \
|
||||
throw std::runtime_error(from "has been renamed to " to); \
|
||||
}
|
||||
|
||||
renamed("include-dirs", "include-directories");
|
||||
renamed("link-libs", "link-libraries");
|
||||
renamed("defines", "compile-definitions");
|
||||
renamed("features", "compile-features");
|
||||
|
||||
#undef renamed
|
||||
|
||||
target.compile_definitions = optional_array(t, "compile-definitions");
|
||||
target.compile_features = optional_array(t, "compile-features");
|
||||
target.compile_options = optional_array(t, "compile-options");
|
||||
target.include_directories = optional_array(t, "include-directories");
|
||||
target.link_directories = optional_array(t, "link-directories");
|
||||
target.link_libraries = optional_array(t, "link-libraries");
|
||||
target.link_options = optional_array(t, "link-options");
|
||||
target.precompile_headers = optional_array(t, "precompile-headers");
|
||||
|
||||
if (t.contains("alias")) {
|
||||
target.alias = toml::find(t, "alias").as_string();
|
||||
}
|
||||
|
||||
if (t.contains("properties")) {
|
||||
using prop_map = tsl::ordered_map<std::string, std::string>;
|
||||
target.properties = toml::find<prop_map>(t, "properties");
|
||||
}
|
||||
|
||||
target.cmake_before = toml::find_or(t, "cmake-before", "");
|
||||
target.cmake_after = toml::find_or(t, "cmake-after", "");
|
||||
target.include_before = optional_array(t, "include-before");
|
||||
target.include_after = optional_array(t, "include-after");
|
||||
|
||||
targets.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("test")) {
|
||||
const auto &ts = toml::find(toml, "test").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Test test;
|
||||
test.name = toml::find(t, "name").as_string();
|
||||
test.cmd = toml::find(t, "type").as_string();
|
||||
test.args = optional_array(t, "arguments");
|
||||
tests.push_back(test);
|
||||
}
|
||||
}
|
||||
|
||||
if (toml.contains("install")) {
|
||||
const auto &ts = toml::find(toml, "install").as_array();
|
||||
for (const auto &t : ts) {
|
||||
Install inst;
|
||||
inst.targets = optional_array(t, "targets");
|
||||
inst.files = optional_array(t, "files");
|
||||
inst.dirs = optional_array(t, "dirs");
|
||||
inst.configs = optional_array(t, "configs");
|
||||
inst.destination = toml::find(t, "destination").as_string();
|
||||
installs.push_back(inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace cmake
|
||||
} // namespace cmkr
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "error.h"
|
||||
#include "error.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
+225
-102
@@ -1,7 +1,7 @@
|
||||
#include "gen.h"
|
||||
#include "gen.hpp"
|
||||
#include "cmake.hpp"
|
||||
#include "error.h"
|
||||
#include "literals.h"
|
||||
#include "error.hpp"
|
||||
#include "literals.hpp"
|
||||
|
||||
#include "fs.hpp"
|
||||
#include <cassert>
|
||||
@@ -41,7 +41,6 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
std::vector<std::string> temp;
|
||||
|
||||
auto extract_suffix = [](const fs::path &base, const fs::path &full) {
|
||||
// TODO: delet this
|
||||
auto fullpath = full.string();
|
||||
auto base_len = base.string().length();
|
||||
auto delet = fullpath.substr(base_len + 1, fullpath.length() - base_len);
|
||||
@@ -70,6 +69,8 @@ static std::vector<std::string> expand_cmake_path(const fs::path &name, const fs
|
||||
for (auto &path : temp) {
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
}
|
||||
// Sort paths alphabetically for consistent cross-OS generation
|
||||
std::sort(temp.begin(), temp.end());
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -139,6 +140,13 @@ struct CommandEndl {
|
||||
void endl() { ss << '\n'; }
|
||||
};
|
||||
|
||||
struct RawArg {
|
||||
RawArg() = default;
|
||||
RawArg(std::string arg) : arg(std::move(arg)) {}
|
||||
|
||||
std::string arg;
|
||||
};
|
||||
|
||||
// Credit: JustMagic
|
||||
struct Command {
|
||||
std::stringstream &ss;
|
||||
@@ -158,7 +166,8 @@ struct Command {
|
||||
|
||||
std::string quote(const std::string &str) {
|
||||
// Don't quote arguments that don't need quoting
|
||||
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos) {
|
||||
if (str.find(' ') == std::string::npos && str.find('\"') == std::string::npos && str.find('/') == std::string::npos &&
|
||||
str.find(';') == std::string::npos) {
|
||||
return str;
|
||||
}
|
||||
std::string result;
|
||||
@@ -231,10 +240,6 @@ struct Command {
|
||||
|
||||
template <class K, class V>
|
||||
bool print_arg(const std::pair<K, V> &kv) {
|
||||
std::stringstream tmp;
|
||||
tmp << kv.second;
|
||||
auto str = tmp.str();
|
||||
|
||||
if (kv.second.empty()) {
|
||||
return true;
|
||||
}
|
||||
@@ -242,14 +247,32 @@ struct Command {
|
||||
had_newline = true;
|
||||
print_arg(kv.first);
|
||||
depth++;
|
||||
print_arg(str);
|
||||
print_arg(kv.second);
|
||||
depth--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool print_arg(const RawArg &arg) {
|
||||
if (arg.arg.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (had_newline) {
|
||||
first_arg = false;
|
||||
ss << '\n' << indent(depth + 1);
|
||||
} else if (first_arg) {
|
||||
first_arg = false;
|
||||
} else {
|
||||
ss << ' ';
|
||||
}
|
||||
|
||||
ss << arg.arg;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool print_arg(const T &value, bool indentation = true) {
|
||||
bool print_arg(const T &value) {
|
||||
std::stringstream tmp;
|
||||
tmp << value;
|
||||
auto str = tmp.str();
|
||||
@@ -257,15 +280,13 @@ struct Command {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (indentation) {
|
||||
if (had_newline) {
|
||||
first_arg = false;
|
||||
ss << '\n' << indent(depth + 1);
|
||||
} else if (first_arg) {
|
||||
first_arg = false;
|
||||
} else {
|
||||
ss << ' ';
|
||||
}
|
||||
if (had_newline) {
|
||||
first_arg = false;
|
||||
ss << '\n' << indent(depth + 1);
|
||||
} else if (first_arg) {
|
||||
first_arg = false;
|
||||
} else {
|
||||
ss << ' ';
|
||||
}
|
||||
|
||||
ss << quote(str);
|
||||
@@ -276,23 +297,33 @@ struct Command {
|
||||
CommandEndl operator()(Ts &&...values) {
|
||||
generated = true;
|
||||
ss << indent(depth) << command << '(';
|
||||
std::initializer_list<bool>{print_arg(values)...};
|
||||
(void)std::initializer_list<bool>{print_arg(values)...};
|
||||
if (had_newline)
|
||||
ss << '\n';
|
||||
ss << '\n' << indent(depth);
|
||||
ss << ")\n";
|
||||
return CommandEndl(ss);
|
||||
}
|
||||
};
|
||||
|
||||
int generate_cmake(const char *path, bool root) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml found!");
|
||||
static std::string tolf(const std::string &str) {
|
||||
std::string result;
|
||||
for (char ch : str) {
|
||||
if (ch != '\r') {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// Helper lambdas for more convenient CMake generation
|
||||
struct Generator {
|
||||
Generator(cmake::CMake &cmake) : cmake(cmake) {}
|
||||
Generator(const Generator &) = delete;
|
||||
|
||||
cmake::CMake &cmake;
|
||||
std::stringstream ss;
|
||||
int indent = 0;
|
||||
auto cmd = [&ss, &indent](const std::string &command) {
|
||||
|
||||
Command cmd(const std::string &command) {
|
||||
if (command.empty())
|
||||
throw std::invalid_argument("command cannot be empty");
|
||||
if (command == "if") {
|
||||
@@ -304,43 +335,93 @@ int generate_cmake(const char *path, bool root) {
|
||||
indent--;
|
||||
}
|
||||
return Command(ss, indent, command);
|
||||
};
|
||||
auto comment = [&ss, &indent](const std::string &comment) {
|
||||
}
|
||||
|
||||
CommandEndl comment(const std::string &comment) {
|
||||
ss << Command::indent(indent) << "# " << comment << '\n';
|
||||
return CommandEndl(ss);
|
||||
};
|
||||
auto endl = [&ss]() { ss << '\n'; };
|
||||
auto tolf = [](const std::string &str) {
|
||||
std::string result;
|
||||
for (char ch : str) {
|
||||
if (ch != '\r') {
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
auto inject_includes = [&cmd, &endl](const std::vector<std::string> &includes) {
|
||||
}
|
||||
|
||||
void endl() { ss << '\n'; }
|
||||
|
||||
void inject_includes(const std::vector<std::string> &includes) {
|
||||
if (!includes.empty()) {
|
||||
for (const auto &file : includes) {
|
||||
// TODO: warn/error if file doesn't exist?
|
||||
if (!fs::is_regular_file(file)) {
|
||||
throw std::runtime_error("Include '" + file + "' does not exist");
|
||||
}
|
||||
cmd("include")(file);
|
||||
}
|
||||
endl();
|
||||
}
|
||||
};
|
||||
auto inject_cmake = [&ss, &tolf](const std::string &cmake) {
|
||||
}
|
||||
|
||||
void inject_cmake(const std::string &cmake) {
|
||||
if (!cmake.empty()) {
|
||||
if (cmake.back() == '\"') {
|
||||
throw std::runtime_error("Detected additional \" at the end of cmake block");
|
||||
}
|
||||
ss << tolf(cmake) << "\n\n";
|
||||
auto cmake_lf = tolf(cmake);
|
||||
while (cmake_lf.back() == '\n')
|
||||
cmake_lf.pop_back();
|
||||
bool did_indent = false;
|
||||
for (char ch : cmake_lf) {
|
||||
if (!did_indent) {
|
||||
ss << Command::indent(indent);
|
||||
did_indent = true;
|
||||
} else if (ch == '\n') {
|
||||
did_indent = false;
|
||||
}
|
||||
ss << ch;
|
||||
}
|
||||
ss << '\n';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: add link with proper documentation
|
||||
comment("This file was generated automatically by cmkr.").endl();
|
||||
template <typename T, typename Lambda>
|
||||
void handle_condition(const cmake::Condition<T> &value, const Lambda &fn) {
|
||||
if (!value.empty()) {
|
||||
for (const auto &itr : value) {
|
||||
const auto &condition = itr.first;
|
||||
if (!condition.empty()) {
|
||||
if (cmake.conditions.count(condition) == 0) {
|
||||
// TODO: somehow print line number information here?
|
||||
throw std::runtime_error("Unknown condition '" + condition + "'");
|
||||
}
|
||||
cmd("if")(RawArg(cmake.conditions[condition]));
|
||||
}
|
||||
|
||||
if (!itr.second.empty()) {
|
||||
fn(condition, itr.second);
|
||||
}
|
||||
|
||||
if (!condition.empty()) {
|
||||
cmd("endif")();
|
||||
}
|
||||
endl();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int generate_cmake(const char *path, bool root) {
|
||||
if (!fs::exists(fs::path(path) / "cmake.toml")) {
|
||||
throw std::runtime_error("No cmake.toml found!");
|
||||
}
|
||||
|
||||
cmake::CMake cmake(path, false);
|
||||
Generator gen(cmake);
|
||||
|
||||
// Helper lambdas for more convenient CMake generation
|
||||
auto &ss = gen.ss;
|
||||
auto cmd = [&gen](const std::string &comment) { return gen.cmd(comment); };
|
||||
auto comment = [&gen](const std::string &comment) { return gen.comment(comment); };
|
||||
auto endl = [&gen]() { gen.endl(); };
|
||||
auto inject_includes = [&gen](const std::vector<std::string> &includes) { gen.inject_includes(includes); };
|
||||
auto inject_cmake = [&gen](const std::string &cmake) { gen.inject_cmake(cmake); };
|
||||
|
||||
comment("This file is automatically generated from cmake.toml - DO NOT EDIT");
|
||||
comment("See https://github.com/build-cpp/cmkr for more information");
|
||||
endl();
|
||||
|
||||
if (root) {
|
||||
cmd("cmake_minimum_required")("VERSION", cmake.cmake_version).endl();
|
||||
@@ -397,8 +478,8 @@ int generate_cmake(const char *path, bool root) {
|
||||
ss << "\")\n\n";
|
||||
}
|
||||
|
||||
inject_includes(cmake.include_before);
|
||||
inject_cmake(cmake.cmake_before);
|
||||
gen.handle_condition(cmake.include_before, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(cmake.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
|
||||
if (!cmake.project_name.empty()) {
|
||||
auto languages = std::make_pair("LANGUAGES", cmake.project_languages);
|
||||
@@ -407,19 +488,8 @@ int generate_cmake(const char *path, bool root) {
|
||||
cmd("project")(cmake.project_name, languages, version, description).endl();
|
||||
}
|
||||
|
||||
inject_includes(cmake.include_after);
|
||||
inject_cmake(cmake.cmake_after);
|
||||
|
||||
if (!cmake.packages.empty()) {
|
||||
for (const auto &dep : cmake.packages) {
|
||||
auto version = dep.version;
|
||||
if (version == "*")
|
||||
version.clear();
|
||||
auto required = dep.required ? "REQUIRED" : "";
|
||||
auto components = std::make_pair("COMPONENTS", dep.components);
|
||||
cmd("find_package")(dep.name, version, required, components).endl();
|
||||
}
|
||||
}
|
||||
gen.handle_condition(cmake.include_after, [&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(cmake.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
|
||||
if (!cmake.contents.empty()) {
|
||||
cmd("include")("FetchContent").endl();
|
||||
@@ -450,7 +520,32 @@ int generate_cmake(const char *path, bool root) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.vcpkg.version.empty()) {
|
||||
assert("pmm is required in fetch-content for vcpkg to work" && cmake.contents.count("pmm") != 0);
|
||||
comment("Bootstrap vcpkg");
|
||||
cmd("include")("${pmm_SOURCE_DIR}/pmm.cmake");
|
||||
tsl::ordered_map<std::string, std::vector<std::string>> vcpkg_args;
|
||||
vcpkg_args["REVISION"] = {cmake.vcpkg.version};
|
||||
vcpkg_args["REQUIRES"] = cmake.vcpkg.packages;
|
||||
auto vcpkg = std::make_pair("VCPKG", vcpkg_args);
|
||||
cmd("pmm")(vcpkg).endl();
|
||||
}
|
||||
|
||||
if (!cmake.packages.empty()) {
|
||||
comment("Packages");
|
||||
for (const auto &dep : cmake.packages) {
|
||||
auto version = dep.version;
|
||||
if (version == "*")
|
||||
version.clear();
|
||||
auto required = dep.required ? "REQUIRED" : "";
|
||||
auto config = dep.config ? "CONFIG" : "";
|
||||
auto components = std::make_pair("COMPONENTS", dep.components);
|
||||
cmd("find_package")(dep.name, version, required, config, components).endl();
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.options.empty()) {
|
||||
comment("Options");
|
||||
for (const auto &opt : cmake.options) {
|
||||
cmd("option")(opt.name, opt.comment, opt.val ? "ON" : "OFF");
|
||||
}
|
||||
@@ -458,6 +553,7 @@ int generate_cmake(const char *path, bool root) {
|
||||
}
|
||||
|
||||
if (!cmake.settings.empty()) {
|
||||
comment("Settings");
|
||||
for (const auto &set : cmake.settings) {
|
||||
std::string set_val;
|
||||
if (set.val.index() == 1) {
|
||||
@@ -479,41 +575,55 @@ int generate_cmake(const char *path, bool root) {
|
||||
|
||||
// generate_cmake is called on the subdirectories recursively later
|
||||
if (!cmake.subdirs.empty()) {
|
||||
for (const auto &dir : cmake.subdirs) {
|
||||
// clang-format off
|
||||
comment(dir);
|
||||
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
||||
cmd("if")("CMAKE_FOLDER");
|
||||
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
||||
cmd("else")();
|
||||
cmd("set")("CMAKE_FOLDER", dir);
|
||||
cmd("endif")();
|
||||
// clang-format on
|
||||
cmd("add_subdirectory")(dir);
|
||||
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
||||
}
|
||||
gen.handle_condition(cmake.subdirs, [&](const std::string &, const std::vector<std::string> &subdirs) {
|
||||
for (const auto &dir : subdirs) {
|
||||
// clang-format off
|
||||
comment(dir);
|
||||
cmd("set")("CMKR_CMAKE_FOLDER", "${CMAKE_FOLDER}");
|
||||
cmd("if")("CMAKE_FOLDER");
|
||||
cmd("set")("CMAKE_FOLDER", "${CMAKE_FOLDER}/" + dir);
|
||||
cmd("else")();
|
||||
cmd("set")("CMAKE_FOLDER", dir);
|
||||
cmd("endif")();
|
||||
// clang-format on
|
||||
|
||||
cmd("add_subdirectory")(dir);
|
||||
cmd("set")("CMAKE_FOLDER", "${CMKR_CMAKE_FOLDER}").endl();
|
||||
}
|
||||
});
|
||||
endl();
|
||||
}
|
||||
|
||||
if (!cmake.targets.empty()) {
|
||||
for (const auto &target : cmake.targets) {
|
||||
comment("Target " + target.name);
|
||||
inject_includes(target.include_before);
|
||||
inject_cmake(target.cmake_before);
|
||||
cmd("set")("CMKR_TARGET", target.name);
|
||||
|
||||
if (!target.sources.empty()) {
|
||||
auto sources = expand_cmake_paths(target.sources, path);
|
||||
gen.handle_condition(target.include_before,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(target.cmake_before, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
|
||||
auto sources_var = target.name + "_SOURCES";
|
||||
|
||||
bool added_toml = false;
|
||||
cmd("set")(sources_var, RawArg("\"\"")).endl();
|
||||
gen.handle_condition(target.sources, [&](const std::string &condition, const std::vector<std::string> &condition_sources) {
|
||||
auto sources = expand_cmake_paths(condition_sources, path);
|
||||
if (sources.empty()) {
|
||||
throw std::runtime_error(target.name + " sources wildcard found 0 files");
|
||||
auto source_key = condition.empty() ? "sources" : (condition + ".sources");
|
||||
throw std::runtime_error(target.name + " " + source_key + " wildcard found 0 files");
|
||||
}
|
||||
if (target.type != cmake::target_interface) {
|
||||
// Do not add cmake.toml twice
|
||||
if (std::find(sources.begin(), sources.end(), "cmake.toml") == sources.end()) {
|
||||
sources.push_back("cmake.toml");
|
||||
}
|
||||
// Do not add cmake.toml twice
|
||||
if (!added_toml && std::find(sources.begin(), sources.end(), "cmake.toml") != sources.end()) {
|
||||
added_toml = true;
|
||||
}
|
||||
cmd("set")(target.name + "_SOURCES", sources).endl();
|
||||
cmd("list")("APPEND", sources_var, sources);
|
||||
});
|
||||
|
||||
if (!added_toml && target.type != cmake::target_interface) {
|
||||
cmd("list")("APPEND", sources_var, std::vector<std::string>{"cmake.toml"}).endl();
|
||||
}
|
||||
cmd("set")("CMKR_SOURCES", "${" + sources_var + "}");
|
||||
|
||||
std::string add_command;
|
||||
std::string target_type;
|
||||
@@ -554,7 +664,13 @@ int generate_cmake(const char *path, bool root) {
|
||||
assert("Unimplemented enum value" && false);
|
||||
}
|
||||
|
||||
cmd(add_command)(target.name, target_type, "${" + target.name + "_SOURCES}").endl();
|
||||
cmd(add_command)(target.name, target_type).endl();
|
||||
|
||||
// clang-format off
|
||||
cmd("if")(sources_var);
|
||||
cmd("target_sources")(target.name, target.type == cmake::target_interface ? "INTERFACE" : "PRIVATE", "${" + sources_var + "}");
|
||||
cmd("endif")().endl();
|
||||
// clang-format on
|
||||
|
||||
// The first executable target will become the Visual Studio startup project
|
||||
if (target.type == cmake::target_executable) {
|
||||
@@ -574,10 +690,9 @@ int generate_cmake(const char *path, bool root) {
|
||||
cmd("add_library")(target.alias, "ALIAS", target.name);
|
||||
}
|
||||
|
||||
auto target_cmd = [&](const char *command, const std::vector<std::string> &args) {
|
||||
if (!args.empty()) {
|
||||
cmd(command)(target.name, target_scope, args).endl();
|
||||
}
|
||||
auto target_cmd = [&](const char *command, const cmake::ConditionVector &cargs) {
|
||||
gen.handle_condition(
|
||||
cargs, [&](const std::string &, const std::vector<std::string> &args) { cmd(command)(target.name, target_scope, args); });
|
||||
};
|
||||
|
||||
target_cmd("target_compile_definitions", target.compile_definitions);
|
||||
@@ -592,18 +707,24 @@ int generate_cmake(const char *path, bool root) {
|
||||
cmd("set_target_properties")(target.name, "PROPERTIES", target.properties).endl();
|
||||
}
|
||||
|
||||
inject_includes(target.include_after);
|
||||
inject_cmake(target.cmake_after);
|
||||
gen.handle_condition(target.include_after,
|
||||
[&](const std::string &, const std::vector<std::string> &includes) { inject_includes(includes); });
|
||||
gen.handle_condition(target.cmake_after, [&](const std::string &, const std::string &cmake) { inject_cmake(cmake); });
|
||||
|
||||
cmd("unset")("CMKR_TARGET");
|
||||
cmd("unset")("CMKR_SOURCES");
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmake.tests.empty()) {
|
||||
cmd("include")("CTest");
|
||||
cmd("enable_testing")().endl();
|
||||
for (const auto &test : cmake.tests) {
|
||||
auto name = std::make_pair("NAME", test.name);
|
||||
auto command = std::make_pair("COMMAND", test.cmd);
|
||||
cmd("add_test")(name, command, test.args).endl();
|
||||
auto configurations = std::make_pair("CONFIGURATIONS", test.configurations);
|
||||
auto working_directory = std::make_pair("WORKING_DIRECTORY", test.working_directory);
|
||||
auto command = std::make_pair("COMMAND", test.command);
|
||||
auto arguments = std::make_pair("", test.arguments);
|
||||
cmd("add_test")(name, configurations, working_directory, command, arguments).endl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,10 +772,12 @@ int generate_cmake(const char *path, bool root) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &sub : cmake.subdirs) {
|
||||
auto subpath = fs::path(path) / fs::path(sub);
|
||||
if (fs::exists(subpath / "cmake.toml"))
|
||||
generate_cmake(subpath.string().c_str(), false);
|
||||
for (const auto &itr : cmake.subdirs) {
|
||||
for (const auto &sub : itr.second) {
|
||||
auto subpath = fs::path(path) / fs::path(sub);
|
||||
if (fs::exists(subpath / "cmake.toml"))
|
||||
generate_cmake(subpath.string().c_str(), false);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "help.h"
|
||||
#include "help.hpp"
|
||||
|
||||
namespace cmkr {
|
||||
namespace help {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#include "args.h"
|
||||
#include "args.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# These will be generated by cmkr, so no point in tracking them
|
||||
**/CMakeLists.txt
|
||||
Generated
+40
@@ -0,0 +1,40 @@
|
||||
# This file is automatically generated from cmake.toml - DO NOT EDIT
|
||||
# See https://github.com/build-cpp/cmkr for more information
|
||||
|
||||
# Create a configure-time dependency on cmake.toml to improve IDE support
|
||||
if(CMKR_ROOT_PROJECT)
|
||||
configure_file(cmake.toml cmake.toml COPYONLY)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
basic
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
conditions
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/conditions"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
interface
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_LIST_DIR}/interface"
|
||||
COMMAND
|
||||
$<TARGET_FILE:cmkr>
|
||||
build
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[cmake]
|
||||
version = "3.5"
|
||||
|
||||
[project]
|
||||
name = "basic"
|
||||
|
||||
[target.basic]
|
||||
type = "executable"
|
||||
sources = ["src/basic.cpp"]
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[[test]]
|
||||
name = "basic"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "${CMAKE_CURRENT_LIST_DIR}/basic"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "conditions"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "${CMAKE_CURRENT_LIST_DIR}/conditions"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "interface"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "${CMAKE_CURRENT_LIST_DIR}/interface"
|
||||
arguments = ["build"]
|
||||
@@ -0,0 +1,17 @@
|
||||
[project]
|
||||
name = "conditions"
|
||||
cmake-after = "set(CUSTOM ON)"
|
||||
|
||||
[conditions]
|
||||
custom = "CUSTOM"
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.sources = ["src/windows_specific.cpp"]
|
||||
cmake-after = "message(STATUS cmake-after)"
|
||||
windows.cmake-after = "message(STATUS win32-after)"
|
||||
macos.cmake-after = "message(STATUS macos-after)"
|
||||
linux.cmake-after = "message(STATUS linux-after)"
|
||||
unix.cmake-after = "message(STATUS unix-after)"
|
||||
custom.cmake-after = "message(STATUS custom-after)"
|
||||
@@ -0,0 +1 @@
|
||||
int main() { }
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() { }
|
||||
@@ -0,0 +1,11 @@
|
||||
[project]
|
||||
name = "interface"
|
||||
|
||||
[target.mylib]
|
||||
type = "interface"
|
||||
include-directories = ["include"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
link-libraries = ["mylib"]
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace mylib
|
||||
{
|
||||
static const char* version() { return "v1.0"; }
|
||||
} // namespace mylib
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include "mylib/mylib.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("mylib version: %s\n", mylib::version())
|
||||
}
|
||||
Reference in New Issue
Block a user