Compare commits
No commits in common. 'main' and 'toml-checker' have entirely different histories.
main
...
toml-check
@ -1,24 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{c,h,cpp,hpp,toml}]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
|
||||
[{CMakeLists.txt, *.cmake}]
|
||||
indent_style = tab
|
||||
tab_width = 8
|
||||
insert_final_newline = true
|
||||
|
||||
# Exclude the third_party folder
|
||||
[/third_party/**]
|
||||
charset = unset
|
||||
end_of_line = unset
|
||||
insert_final_newline = unset
|
||||
trim_trailing_whitespace = unset
|
||||
indent_style = unset
|
||||
indent_size = unset
|
@ -1 +0,0 @@
|
||||
github: [mrexodia]
|
@ -1,41 +0,0 @@
|
||||
name: lint
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: clang-format
|
||||
id: clang-format
|
||||
uses: DoozyX/clang-format-lint-action@c3b2c943e924028b93a707a5b1b017976ab8d50c # v0.15
|
||||
with:
|
||||
exclude: './third_party'
|
||||
extensions: 'c,h,cpp,hpp'
|
||||
clangFormatVersion: 15
|
||||
style: file
|
||||
|
||||
- name: clang-format instructions
|
||||
if: ${{ failure() && steps.clang-format.outcome == 'failure' }}
|
||||
run: |
|
||||
# Instructions for fixing the formatting errors
|
||||
echo -e "\n\033[0;31mTo fix the formatting, run:\nclang-format -style=file -i \$(git ls-files \"*.c\" \"*.h\" \"*.cpp\" \"*.hpp\")\033[0m\n"
|
||||
exit 1
|
||||
|
||||
editorconfig:
|
||||
# Skip building pull requests from the same repository
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run editorconfig-checker
|
||||
uses: editorconfig-checker/action-editorconfig-checker@d4fca16fc71adef10fbe101903b654449fa9570c # master 2022-03-15
|
@ -1,78 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
if(NOT CMAKE_SCRIPT_MODE_FILE)
|
||||
message(FATAL_ERROR "Usage: cmake -P bump_version.cmake [1.2.3]")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
|
||||
message(FATAL_ERROR "Cannot find cmake.toml")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake")
|
||||
message(FATAL_ERROR "Cannot find cmkr.cmake")
|
||||
endif()
|
||||
|
||||
# Validate branch
|
||||
find_package(Git REQUIRED)
|
||||
execute_process(COMMAND "${GIT_EXECUTABLE}" branch --show-current OUTPUT_VARIABLE GIT_BRANCH)
|
||||
string(STRIP "${GIT_BRANCH}" GIT_BRANCH)
|
||||
if(NOT GIT_BRANCH STREQUAL "main")
|
||||
message(FATAL_ERROR "You need to be on the main branch, you are on: ${GIT_BRANCH}")
|
||||
endif()
|
||||
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake.toml" CMAKE_TOML)
|
||||
string(FIND "${CMAKE_TOML}" "[project]" PROJECT_INDEX)
|
||||
string(SUBSTRING "${CMAKE_TOML}" ${PROJECT_INDEX} -1 CMAKE_TOML_PROJECT)
|
||||
set(SEMVER_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
|
||||
set(VERSION_REGEX "version = \"${SEMVER_REGEX}\"")
|
||||
if(CMAKE_TOML_PROJECT MATCHES "${VERSION_REGEX}")
|
||||
set(MAJOR "${CMAKE_MATCH_1}")
|
||||
set(MINOR "${CMAKE_MATCH_2}")
|
||||
set(PATCH "${CMAKE_MATCH_3}")
|
||||
set(OLDVERSION "${MAJOR}.${MINOR}.${PATCH}")
|
||||
else()
|
||||
message(FATAL_ERROR "Failed to match semantic version in cmake.toml")
|
||||
endif()
|
||||
|
||||
if(CMAKE_ARGV3)
|
||||
if(NOT CMAKE_ARGV3 MATCHES "${SEMVER_REGEX}")
|
||||
message(FATAL_ERROR "Invalid semantic version number '${CMAKE_ARGV3}'")
|
||||
endif()
|
||||
set(NEWVERSION "${CMAKE_ARGV3}")
|
||||
else()
|
||||
math(EXPR NEWPATCH "${PATCH} + 1")
|
||||
set(NEWVERSION "${MAJOR}.${MINOR}.${NEWPATCH}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Version ${OLDVERSION} -> ${NEWVERSION}")
|
||||
|
||||
find_program(CMKR_EXECUTABLE "cmkr" PATHS "${CMAKE_SOURCE_DIR}/build" PATH_SUFFIXES Debug Release RelWithDebInfo MinSizeRel NO_CACHE REQUIRED)
|
||||
message(STATUS "Found cmkr: ${CMKR_EXECUTABLE}")
|
||||
|
||||
# Replace version in cmake.toml
|
||||
string(REPLACE "version = \"${OLDVERSION}\"" "version = \"${NEWVERSION}\"" CMAKE_TOML "${CMAKE_TOML}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake.toml"
|
||||
CONTENT "${CMAKE_TOML}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Run cmkr gen
|
||||
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen RESULT_VARIABLE CMKR_EXEC_RESULT)
|
||||
if(NOT CMKR_EXEC_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "cmkr gen failed (exit code ${CMKR_EXEC_RESULT})")
|
||||
endif()
|
||||
|
||||
# Replace version in cmkr.cmake
|
||||
file(READ "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake" CMKR_CMAKE)
|
||||
string(REGEX REPLACE "CMKR_TAG \"[^\"]+\"" "CMKR_TAG \"v${NEWVERSION}\"" CMKR_CMAKE "${CMKR_CMAKE}")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CMAKE_SOURCE_DIR}/cmake/cmkr.cmake"
|
||||
CONTENT "${CMKR_CMAKE}"
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
|
||||
# Print git commands
|
||||
message(STATUS "Git commands to create new version:\ngit commit -a -m \"Bump to ${NEWVERSION}\"\ngit tag v${NEWVERSION}\ngit push origin main v${NEWVERSION}")
|
@ -1,18 +0,0 @@
|
||||
generate_resources(cmkr)
|
||||
|
||||
add_custom_target(regenerate-cmake
|
||||
COMMAND "$<TARGET_FILE:cmkr>" gen
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}" -C $<CONFIG>
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
else()
|
||||
add_custom_target(run-tests
|
||||
COMMAND "${CMAKE_CTEST_COMMAND}"
|
||||
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/tests"
|
||||
)
|
||||
endif()
|
@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#define CMKR_VERSION "@PROJECT_VERSION@"
|
@ -1 +0,0 @@
|
||||
cmkr.build
|
@ -1,76 +0,0 @@
|
||||
---
|
||||
layout: page
|
||||
title: Basics
|
||||
nav_order: 1
|
||||
---
|
||||
|
||||
# Basics
|
||||
|
||||
To effectively use cmkr it helps to understand the basic concepts of CMake.
|
||||
|
||||
## Projects
|
||||
|
||||
A CMake **project** is a collection of targets. In the context of libraries the project usually corresponds to the **package** that other projects can depend on.
|
||||
|
||||
<sub>Visual Studio: a CMake **project** corresponds to a _solution_.</sub>
|
||||
|
||||
## Targets
|
||||
|
||||
The basic unit of CMake is called a **target**. A target (also referred to as [binary target](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#binary-targets) in the CMake documentation) corresponds to an executable or library you can build. There are also [pseudo targets](https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#pseudo-targets), but we ignore them for now.
|
||||
|
||||
<sub>Visual Studio: a **target** corresponds to a _project_.</sub>
|
||||
|
||||
## Target Properties
|
||||
|
||||
Targets have a collection of **properties** that describe how to build them.
|
||||
|
||||
Examples of properties:
|
||||
|
||||
- _Sources_: the `*.cpp` files used to build the target.
|
||||
- _Compile options_: Command line flags for the compiler.
|
||||
- _Link libraries_: The **dependencies** required to build this target.
|
||||
|
||||
<sub>See the [CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#properties-on-targets) for an exhaustive list of target properties.</sub>
|
||||
|
||||
**Important**: The term **link** has a slightly different meaning in CMake than you might expect. In addition to adding a library to the command line of the linker, CMake also propagates properties of the target you link to.
|
||||
|
||||
<sub>You can think of **linking** as _depending on_.</sub>
|
||||
|
||||
The propagation of properties depends on their **visibility**:
|
||||
|
||||
- **Private**: properties that are only used when building the target itself.
|
||||
- **Interface**: properties that are used when depending on this target.
|
||||
- **Public**: combination of private and interface.
|
||||
|
||||
In practice you default to **private**, unless consumers of your library _require_ the property to build their target. In that case you use **public**.
|
||||
|
||||
### Example
|
||||
|
||||
The most intuitive example is with _include directories_. Imagine there are two targets:
|
||||
|
||||
1. `StringUtils`: A library with string utilities.
|
||||
- _Sources_: `StringUtils/src/stringutils.cpp`
|
||||
- _Include directories_: `StringUtils/include`
|
||||
2. `DataProcessor`: An executable that uses functionality from `StringUtils` to process some data.
|
||||
- _Sources_: `DataProcessor/src/main.cpp`
|
||||
- _Link libraries_: `StringUtils`
|
||||
|
||||
The _include directories_ property of `StringUtils` has to be **public**. If it was **private**, the `DataProcessor` target would fail to `#include <stringutils.hpp>` since the _include directories_ property is not propagated.
|
||||
|
||||
The `cmake.toml` for this example would look something like this:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "DataProcessor"
|
||||
|
||||
[target.StringUtils]
|
||||
type = "static"
|
||||
sources = ["StringUtils/src/stringutils.cpp"]
|
||||
headers = ["StringUtils/include/stringutils.hpp"]
|
||||
include-directories = ["StringUtils/include"]
|
||||
|
||||
[target.DataProcessor]
|
||||
type = "executable"
|
||||
sources = ["DataProcessor/src/main.cpp"]
|
||||
link-libraries = ["StringUtils"]
|
||||
```
|
@ -0,0 +1,22 @@
|
||||
---
|
||||
layout: page
|
||||
title: Command line
|
||||
permalink: /command-line/
|
||||
nav_order: 2
|
||||
---
|
||||
|
||||
# Command line
|
||||
|
||||
Optionally you can install `cmkr` in your `PATH` and use it as a utility from the command line:
|
||||
|
||||
```
|
||||
Usage: cmkr [arguments]
|
||||
arguments:
|
||||
init [executable|library|shared|static|interface] Starts a new project in the same directory.
|
||||
gen Generates CMakeLists.txt file.
|
||||
build <extra cmake args> Run cmake and build.
|
||||
install Run cmake --install. Needs admin privileges.
|
||||
clean Clean the build directory.
|
||||
help Show help.
|
||||
version Current cmkr version.
|
||||
```
|
@ -1,31 +0,0 @@
|
||||
---
|
||||
# Automatically generated from tests/compile-options/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Compiler flags
|
||||
permalink: /examples/compile-options
|
||||
parent: Examples
|
||||
nav_order: 9
|
||||
---
|
||||
|
||||
# Compiler flags
|
||||
|
||||
Example project that sets compiler/linker flags for various platforms.
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
clang.compile-options = ["-Wall"]
|
||||
```
|
||||
|
||||
The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
|
||||
_Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/compile-options/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/compile-options/cmake.toml).</sub></sup>
|
@ -1,34 +0,0 @@
|
||||
---
|
||||
# Automatically generated from tests/msvc-runtime/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Static MSVC runtime
|
||||
permalink: /examples/msvc-runtime
|
||||
parent: Examples
|
||||
nav_order: 8
|
||||
---
|
||||
|
||||
# Static MSVC runtime
|
||||
|
||||
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "msvc-runtime"
|
||||
description = "Static MSVC runtime"
|
||||
msvc-runtime = "static"
|
||||
|
||||
# This target will compile with a static runtime
|
||||
[target.static-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
|
||||
# This target overrides the [project].msvc-runtime
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
||||
```
|
||||
|
||||
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/msvc-runtime/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/msvc-runtime/cmake.toml).</sub></sup>
|
@ -1,40 +0,0 @@
|
||||
---
|
||||
# Automatically generated from tests/templates/cmake.toml - DO NOT EDIT
|
||||
layout: default
|
||||
title: Target templates
|
||||
permalink: /examples/templates
|
||||
parent: Examples
|
||||
nav_order: 7
|
||||
---
|
||||
|
||||
# Target templates
|
||||
|
||||
To avoid repeating yourself in targets you can create your own target type (template). All properties of the template are inherited when used as a target type.
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "templates"
|
||||
description = "Target templates"
|
||||
|
||||
[template.app]
|
||||
type = "executable"
|
||||
sources = ["src/templates.cpp"]
|
||||
compile-definitions = ["IS_APP"]
|
||||
|
||||
# Unlike interface targets you can also inherit properties
|
||||
[template.app.properties]
|
||||
CXX_STANDARD = "11"
|
||||
CXX_STANDARD_REQUIRED = true
|
||||
|
||||
[target.app-a]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_A"]
|
||||
|
||||
[target.app-b]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_B"]
|
||||
```
|
||||
|
||||
**Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
||||
|
||||
<sup><sub>This page was automatically generated from [tests/templates/cmake.toml](https://github.com/build-cpp/cmkr/tree/main/tests/templates/cmake.toml).</sub></sup>
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: null
|
||||
permalink: /getting-started/
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=https://cmkr.build">
|
||||
</head>
|
||||
</html>
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
layout: page
|
||||
title: Getting started
|
||||
permalink: /getting-started/
|
||||
nav_order: 1
|
||||
---
|
||||
|
||||
# Getting started
|
||||
|
||||
The easiest way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
|
||||
|
||||
```sh
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Alternatively you can check out the [cmkr topic](https://github.com/topics/cmkr) or the [build-cpp organization](https://github.com/build-cpp) for more examples and templates.
|
@ -1,81 +1,23 @@
|
||||
---
|
||||
layout: home
|
||||
title: Documentation
|
||||
title: Index
|
||||
nav_order: 0
|
||||
---
|
||||
|
||||
# Documentation
|
||||
# Index
|
||||
|
||||
[`cmkr`](https://github.com/build-cpp/cmkr), pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io).
|
||||
`cmkr`, pronounced "cmaker", is a modern build system based on [CMake](https://cmake.org/) and [TOML](https://toml.io). It was originally created by [Mohammed Alyousef](https://github.com/MoAlyousef).
|
||||
|
||||
`cmkr` parses `cmake.toml` files and generates a modern, idiomatic `CMakeLists.txt` for you. A minimal example:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "cmkr_for_beginners"
|
||||
description = "A minimal cmkr project."
|
||||
|
||||
[target.hello_world]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
```
|
||||
|
||||
`cmkr` can bootstrap itself, and you only need CMake and a C++ compiler to use it.
|
||||
|
||||
## Getting started
|
||||
|
||||
To get started, run the following commands from your project directory:
|
||||
|
||||
```sh
|
||||
curl https://raw.githubusercontent.com/build-cpp/cmkr/main/cmake/cmkr.cmake -o cmkr.cmake
|
||||
cmake -P cmkr.cmake
|
||||
```
|
||||
|
||||
After the bootstrapping process finishes, customize [`cmake.toml`](./cmake-toml) for your project and run CMake:
|
||||
|
||||
```sh
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Once bootstrapped, `cmkr` does not introduce extra steps to your workflow. After modifying `cmake.toml` you simply build/configure your CMake project and `cmkr` will automatically regenerate `CMakeLists.txt` when necessary.
|
||||
|
||||
<sub>**Note**: The `cmake.toml` project file, generated `CMakeLists.txt` and `cmkr.cmake` bootstrapping script are all intended to be added to source control.</sub>
|
||||
|
||||
In CI environments the `cmkr` bootstrapping process is skipped, so there is no additional overhead in your pipelines.
|
||||
|
||||
## Template repositories
|
||||
|
||||
Another way to get started is to use the [cmkr_for_beginners](https://github.com/build-cpp/cmkr_for_beginners) template repository. Either open it in [Gitpod](https://gitpod.io/#https://github.com/build-cpp/cmkr_for_beginners), or clone the repository and run:
|
||||
|
||||
```sh
|
||||
cmake -B build
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Check out the [cmkr topic](https://github.com/topics/cmkr), the [build-cpp organization](https://github.com/build-cpp) or the [tests](https://github.com/build-cpp/cmkr/tree/main/tests) for more examples and templates.
|
||||
|
||||
## Command line
|
||||
|
||||
Optionally you can put a [`cmkr` release](https://github.com/build-cpp/cmkr/releases) in your `PATH` and use it as a utility from the command line:
|
||||
|
||||
```
|
||||
Usage: cmkr [arguments]
|
||||
arguments:
|
||||
init [executable|library|shared|static|interface] Create a project.
|
||||
gen Generates CMakeLists.txt file.
|
||||
build <extra cmake args> Run cmake and build.
|
||||
install Run cmake --install.
|
||||
clean Clean the build directory.
|
||||
help Show help.
|
||||
version Current cmkr version.
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
- [gulrak/filesystem](https://github.com/gulrak/filesystem)
|
||||
- [Tessil/ordered-map](https://github.com/Tessil/ordered-map)
|
||||
- [ToruNiina/toml11](https://github.com/ToruNiina/toml11)
|
||||
- [mpark/variant](https://github.com/mpark/variant)
|
||||
- [SVG Repo Hammer](https://www.svgrepo.com/svg/192268/hammer)
|
||||
- [can1357](https://github.com/can1357) for buying `cmkr.build` ❤️
|
||||
- [JustasMasiulis](https://github.com/JustasMasiulis) for fixing the dark theme ❤️
|
||||
`cmkr` can bootstrap itself from CMake and consumers of your project do not need to install anything to work with it.
|
||||
|
@ -1,83 +0,0 @@
|
||||
---
|
||||
layout: page
|
||||
title: Philosophy
|
||||
nav_order: 2
|
||||
published: false
|
||||
---
|
||||
|
||||
# Philosophy
|
||||
|
||||
## Problem statement
|
||||
|
||||
Similar to writing good C++, writing good CMake is difficult. The main difference is that nobody actually wants to learn CMake. The build system is something that should "just work".
|
||||
|
||||
There have been many attempts at creating new build systems (with varying levels of success). Naturally this causes [competing standards](https://xkcd.com/927/), which is undesirable. CMake is pretty much the de facto standard, and it has seen extensive use in complex software projects.
|
||||
|
||||
One of the main issues of CMake is the turing-complete scripting language you use to describe your projects. As your project gets more complex this can be very helpful, but it also unnecessarily complicates simple projects. There have been discussions about a declarative language on the [CMake issue tracker](https://gitlab.kitware.com/cmake/cmake/-/issues/19891) to solve this problem, but it looks like a "LISP-like" language is seriously being considered...
|
||||
|
||||
## The solution
|
||||
|
||||
The way cmkr (pronounced "cmaker") solves this problem is by using [TOML](https://toml.io/). Below is a minimal `cmake.toml` project:
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "cmkr_for_beginners"
|
||||
|
||||
[target.hello_world]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
```
|
||||
|
||||
The key difference between `cmkr` and other build systems is that it _generates_ CMake. This means that your projects are fully compatible with the CMake ecosystem, and you can try it out without having to rewrite your whole build system.
|
||||
|
||||
TODO: link/include more examples? Talk about conditions, packaging (missing)
|
||||
|
||||
### Another layer?!
|
||||
|
||||
A common issue people have with cmkr is that it introduces an additional layer of indirection to your build system. CMake is already a meta-buildsystem, which means you could call cmkr a "meta-meta-buildsystem".
|
||||
|
||||
Presumably the reason for this friction is that additional layers of abstraction introduce additional complexity. Because of this cmkr has been designed to be completely seamless:
|
||||
|
||||
- The user doesn't have to install any additional software to use cmkr. All you need is a semi-recent version of CMake and a C++ compiler.
|
||||
- Modifying `cmake.toml` automatically triggers a regeneration of `CMakeLists.txt`. There is no change to your build process.
|
||||
- The `CMakeLists.txt` is generated to be human-readable. This means you can easily "eject" from cmkr and go back to CMake.
|
||||
|
||||
An additional argument for cmkr is that anecdotally people say it "just works". Because of its simplicity it's also easy to teach, even to people without programming experience.
|
||||
|
||||
There is also precedent in the JavaScript community. Bundlers and translators are the norm there and their developer experience is miles ahead of C++.
|
||||
|
||||
<sub>Not to say the JavaScript ecosystem is without its flaws, but generators does not appear to be one of them</sub>
|
||||
|
||||
### Unsupported features
|
||||
|
||||
Because cmkr is still in early in development there are many missing/unfinished features. It was decided that users can bridge the gap by including CMake at arbitrary locations.
|
||||
|
||||
This has the advantage that it forces complexity in the build system to be self-contained and modular, a problem all too common in projects as they age.
|
||||
|
||||
## Enterprise
|
||||
|
||||
Words like "bootstrapping" and "generating code" might worry engineers working in an enterprise environment, and rightly so. From the beginning it has been a priority to make cmkr suitable for use in big corporations with strict protocols for security and reproducibility.
|
||||
|
||||
### No additional dependencies
|
||||
|
||||
As mentioned above, the only thing you need is a working C++ compiler and a semi-recent version of CMake. It is assumed that you are already building (and executing) C++ projects on your servers, so cmkr does not introduce additional requirements.
|
||||
|
||||
All the logic for downloading and compiling the `cmkr` executable is self-contained in a ~250 line `cmkr.cmake` script. You can easily audit it and see if it's up to your standards.
|
||||
|
||||
### Reproducibility
|
||||
|
||||
Per default the `cmkr.cmake` bootstrapping script contains the exact version of cmkr used to generate your project. As long as the cmkr repository is available you will build the exact same version of cmkr, which will generate the exact same `CMakeLists.txt` file.
|
||||
|
||||
This also means that cmkr can decide to break backwards compatibility without affecting legacy projects. An effort will always be made to maintain backwards compatibility though.
|
||||
|
||||
### Integrity
|
||||
|
||||
As an additional safeguard you can modify `cmkr.cmake` to pin the version tag to a commit hash. This hash is checked to ensure the integrity of the upstream repository.
|
||||
|
||||
### Availability
|
||||
|
||||
You can easily point `cmkr.cmake` to a mirror of the cmkr repository to ensure availability should something catastrophic happen.
|
||||
|
||||
### Not executed in CI
|
||||
|
||||
The final (and key) feature is that the bootstrapping process is never executed in CI environments. This means `cmkr` is only ever executed on your developer's machines and not on your infrastructure.
|
@ -0,0 +1,2 @@
|
||||
bundle exec just-the-docs rake search:init
|
||||
bundle exec jekyll serve --livereload
|
@ -0,0 +1,69 @@
|
||||
// https://codereview.stackexchange.com/a/14315
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// This is the type that will hold all the strings.
|
||||
// Each enumeration type will declare its own specialization.
|
||||
// Any enum that does not have a specialization will generate a compiler error
|
||||
// indicating that there is no definition of this variable (as there should be
|
||||
// be no definition of a generic version).
|
||||
template <typename T>
|
||||
struct enumStrings {
|
||||
static char const *data[];
|
||||
};
|
||||
|
||||
// This is a utility type.
|
||||
// Created automatically. Should not be used directly.
|
||||
template <typename T>
|
||||
struct enumRefHolder {
|
||||
T &enumVal;
|
||||
enumRefHolder(T &enumVal) : enumVal(enumVal) {}
|
||||
};
|
||||
template <typename T>
|
||||
struct enumConstRefHolder {
|
||||
T const &enumVal;
|
||||
enumConstRefHolder(T const &enumVal) : enumVal(enumVal) {}
|
||||
};
|
||||
|
||||
// The next two functions do the actual work of reading/writing an
|
||||
// enum as a string.
|
||||
template <typename T>
|
||||
std::ostream &operator<<(std::ostream &str, enumConstRefHolder<T> const &data) {
|
||||
return str << enumStrings<T>::data[data.enumVal];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::istream &operator>>(std::istream &str, enumRefHolder<T> const &data) {
|
||||
std::string value;
|
||||
str >> value;
|
||||
|
||||
// These two can be made easier to read in C++11
|
||||
// using std::begin() and std::end()
|
||||
//
|
||||
static auto begin = std::begin(enumStrings<T>::data);
|
||||
static auto end = std::end(enumStrings<T>::data);
|
||||
|
||||
auto find = std::find(begin, end, value);
|
||||
if (find != end) {
|
||||
data.enumVal = static_cast<T>(std::distance(begin, find));
|
||||
} else {
|
||||
throw std::invalid_argument("");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
// This is the public interface:
|
||||
// use the ability of function to deduce their template type without
|
||||
// being explicitly told to create the correct type of enumRefHolder<T>
|
||||
template <typename T>
|
||||
enumConstRefHolder<T> enumToString(T const &e) {
|
||||
return enumConstRefHolder<T>(e);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
enumRefHolder<T> enumFromString(T &e) {
|
||||
return enumRefHolder<T>(e);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
struct Status {
|
||||
enum class Code {
|
||||
Success = 0,
|
||||
RuntimeError,
|
||||
InitError,
|
||||
GenerationError,
|
||||
BuildError,
|
||||
CleanError,
|
||||
InstallError,
|
||||
};
|
||||
Status(Code ec) noexcept;
|
||||
operator int() const noexcept;
|
||||
Code code() const noexcept;
|
||||
|
||||
private:
|
||||
Code ec_ = Code::Success;
|
||||
};
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
const char *cmkr_error_status_string(int);
|
@ -1,100 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
static const char *cpp_executable = &R"lit(
|
||||
static const char *hello_world = &R"lit(
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello from cmkr!\n";
|
||||
return EXIT_SUCCESS;
|
||||
int %s() {
|
||||
std::cout << "Hello World!\n";
|
||||
return 0;
|
||||
}
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *cpp_library = &R"lit(
|
||||
#include <@name/@name.hpp>
|
||||
static const char *cmake_toml = &R"lit(
|
||||
[cmake]
|
||||
version = "3.15"
|
||||
# subdirs = []
|
||||
# build-dir = ""
|
||||
# cpp-flags = []
|
||||
# c-flags = []
|
||||
# link-flags = []
|
||||
# generator = ""
|
||||
# arguments = []
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace @name {
|
||||
|
||||
void hello() {
|
||||
std::cout << "Hello from cmkr!\n";
|
||||
}
|
||||
|
||||
} // namespace @name
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *hpp_library = &R"lit(
|
||||
#pragma once
|
||||
|
||||
namespace @name {
|
||||
|
||||
void hello();
|
||||
|
||||
} // namespace @name
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *hpp_interface = &R"lit(
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace @name {
|
||||
|
||||
inline void hello() {
|
||||
std::cout << "Hello from cmkr!\n";
|
||||
}
|
||||
|
||||
} // namespace @name
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *toml_executable = &R"lit(
|
||||
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
|
||||
[project]
|
||||
name = "@name"
|
||||
|
||||
[target.@name]
|
||||
type = "executable"
|
||||
sources = ["src/@name/main.cpp"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
)lit"[1]; // skip initial newline
|
||||
name = "%s"
|
||||
version = "0.1.0"
|
||||
|
||||
static const char *toml_library = &R"lit(
|
||||
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
|
||||
[project]
|
||||
name = "@name"
|
||||
# [find-package]
|
||||
|
||||
[target.@name]
|
||||
type = "@type"
|
||||
sources = [
|
||||
"src/@name/@name.cpp",
|
||||
"include/@name/@name.hpp"
|
||||
]
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
)lit"[1]; // skip initial newline
|
||||
# [fetch-content]
|
||||
|
||||
static const char *toml_interface = &R"lit(
|
||||
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
|
||||
[project]
|
||||
name = "@name"
|
||||
# [options]
|
||||
|
||||
[target.@name]
|
||||
type = "interface"
|
||||
[target.%s]
|
||||
type = "%s"
|
||||
sources = ["src/*.cpp"]
|
||||
include-directories = ["include"]
|
||||
compile-features = ["cxx_std_11"]
|
||||
)lit"[1]; // skip initial newline
|
||||
|
||||
static const char *toml_migration = &R"lit(
|
||||
# Reference: https://build-cpp.github.io/cmkr/cmake-toml
|
||||
[project]
|
||||
name = "@name"
|
||||
|
||||
# TODO: define a target for each of your executables/libraries like this:
|
||||
#[target.myexecutable]
|
||||
#type = "executable" # static, shared
|
||||
#sources = ["src/@name/*.cpp", "include/@name/*.hpp"]
|
||||
#include-directories = ["include"]
|
||||
#compile-features = ["cxx_std_11"]
|
||||
#link-libraries = ["target-or-library"]
|
||||
# alias = ""
|
||||
# compile-features = []
|
||||
# compile-definitions = []
|
||||
# link-libraries = []
|
||||
|
||||
[[install]]
|
||||
%s = ["%s"]
|
||||
destination = "${CMAKE_INSTALL_PREFIX}/%s"
|
||||
)lit"[1]; // skip initial newline
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,25 @@
|
||||
#include "error.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace cmkr {
|
||||
namespace error {
|
||||
|
||||
Status::Status(Code ec) noexcept : ec_(ec) {}
|
||||
|
||||
Status::operator int() const noexcept { return static_cast<int>(ec_); }
|
||||
|
||||
Status::Code Status::code() const noexcept { return ec_; }
|
||||
|
||||
} // namespace error
|
||||
} // namespace cmkr
|
||||
|
||||
// strings for cmkr::error::Status::Code
|
||||
static const char *err_string[] = {
|
||||
"Success", "Runtime error", "Initialization error", "CMake generation error", "Build error", "Clean error", "Install error",
|
||||
};
|
||||
|
||||
const char *cmkr_error_status(int i) {
|
||||
assert(i >= 0 && i < (sizeof(err_string) / sizeof(*(err_string))));
|
||||
return err_string[i];
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +1,41 @@
|
||||
[[test]]
|
||||
name = "basic"
|
||||
working-directory = "basic"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "basic"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "interface"
|
||||
working-directory = "interface"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "interface"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "fetch-content"
|
||||
working-directory = "fetch-content"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "fetch-content"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "conditions"
|
||||
working-directory = "conditions"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "conditions"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "vcpkg"
|
||||
working-directory = "vcpkg"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "vcpkg"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "cxx-standard"
|
||||
working-directory = "cxx-standard"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "cxx-standard"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "globbing"
|
||||
working-directory = "globbing"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "templates"
|
||||
working-directory = "templates"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
condition = "msvc"
|
||||
name = "msvc-runtime"
|
||||
working-directory = "msvc-runtime"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
name = "compile-options"
|
||||
working-directory = "compile-options"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
arguments = ["build"]
|
||||
|
||||
[[test]]
|
||||
condition = "windows"
|
||||
name = "relative-paths"
|
||||
working-directory = "relative-paths"
|
||||
command = "$<TARGET_FILE:cmkr>"
|
||||
working-directory = "globbing"
|
||||
arguments = ["build"]
|
||||
|
@ -1,15 +0,0 @@
|
||||
# Example project that sets compiler/linker flags for various platforms.
|
||||
|
||||
[project]
|
||||
name = "compile-options"
|
||||
description = "Compiler flags"
|
||||
|
||||
[target.hello]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc.compile-options = ["/W2"]
|
||||
gcc.compile-options = ["-Wall"]
|
||||
clang.compile-options = ["-Wall"]
|
||||
|
||||
# The `hello` target uses [conditions](/cmake-toml#conditions) to set different compiler flags depending on the platform. See the [targets](/cmake-toml/#targets) documentation for other things you can set.
|
||||
# _Note_: In general you only want to specify flags _required_ to compile your code without errors.
|
@ -1,5 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr!");
|
||||
}
|
@ -1,2 +1 @@
|
||||
int main() {
|
||||
}
|
||||
int main() { }
|
@ -1,4 +1,3 @@
|
||||
#include <Windows.h>
|
||||
|
||||
void foo() {
|
||||
}
|
||||
void foo() { }
|
@ -1,7 +1,8 @@
|
||||
#include <cstdio>
|
||||
#include <tuple>
|
||||
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
auto tpl = std::make_tuple(1, 2);
|
||||
printf("Hello from C++11 %d\n", std::get<0>(tpl));
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <mylib/mylib.hpp>
|
||||
|
||||
std::string mylib::message() {
|
||||
std::string mylib::message()
|
||||
{
|
||||
return "cmkr is awesome!";
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
namespace mylib {
|
||||
static const char *version() {
|
||||
return "v1.0";
|
||||
}
|
||||
namespace mylib
|
||||
{
|
||||
static const char* version() { return "v1.0"; }
|
||||
} // namespace mylib
|
@ -1,15 +0,0 @@
|
||||
[project]
|
||||
name = "msvc-runtime"
|
||||
description = "Static MSVC runtime"
|
||||
msvc-runtime = "static"
|
||||
|
||||
# This target will compile with a static runtime
|
||||
[target.static-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
|
||||
# This target overrides the [project].msvc-runtime
|
||||
[target.dynamic-runtime]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
msvc-runtime = "dynamic"
|
@ -1,5 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
int main() {
|
||||
puts("Hello from cmkr(msvc-static)!");
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
[project]
|
||||
name = "relative-paths"
|
||||
|
||||
[target.test-library]
|
||||
type = "static"
|
||||
sources = ["src/library-code.cpp"]
|
||||
|
||||
[target.example]
|
||||
type = "executable"
|
||||
sources = ["src/main.cpp"]
|
||||
windows.link-libraries = ["libs/test-library-x64-Release.lib"]
|
Binary file not shown.
@ -1,6 +0,0 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" void library_function() {
|
||||
std::puts("Hello from library_function!");
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// Created by Anthony Printup on 9/18/2023.
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef WIN32
|
||||
extern "C" void library_function();
|
||||
#endif
|
||||
int main() {
|
||||
puts("Hello from cmkr(relative-paths)!");
|
||||
#ifdef WIN32
|
||||
library_function();
|
||||
#endif
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
# To avoid repeating yourself in targets you can create your own target type (template). All properties of the template are inherited when used as a target type.
|
||||
|
||||
[project]
|
||||
name = "templates"
|
||||
description = "Target templates"
|
||||
|
||||
[template.app]
|
||||
type = "executable"
|
||||
sources = ["src/templates.cpp"]
|
||||
compile-definitions = ["IS_APP"]
|
||||
|
||||
# Unlike interface targets you can also inherit properties
|
||||
[template.app.properties]
|
||||
CXX_STANDARD = "11"
|
||||
CXX_STANDARD_REQUIRED = true
|
||||
|
||||
[target.app-a]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_A"]
|
||||
|
||||
[target.app-b]
|
||||
type = "app"
|
||||
compile-definitions = ["APP_B"]
|
||||
|
||||
# **Note**: In most cases you probably want to use an [interface](/examples/interface) target instead.
|
@ -1,13 +0,0 @@
|
||||
#include <cstdio>
|
||||
|
||||
#if !defined(IS_APP)
|
||||
#error Something went wrong with the template
|
||||
#endif // IS_APP
|
||||
|
||||
int main() {
|
||||
#if defined(APP_A)
|
||||
puts("Hello from app A!");
|
||||
#elif defined(APP_B)
|
||||
puts("Hello from app B!");
|
||||
#endif
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
int main() {
|
||||
int main()
|
||||
{
|
||||
fmt::print("Hello, world!\n");
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
# Disable clang-format in this folder
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013-2021 Niels Lohmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue