Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Added CMakeLists.txt and Makefile for clean & fast builds of external projects that use Current. #956

Merged
merged 25 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b08d33c
Preparing a universal `cmake`-based build.
dimacurrentai Jan 2, 2024
906b632
Added the `Makefile` to accompany this `CMakeLists.txt`.
dimacurrentai Jan 2, 2024
28c875d
Added `cmake/run-cmake-test.sh`.
dimacurrentai Jan 2, 2024
baca2bd
Added the `cmake.yml` action.
dimacurrentai Jan 2, 2024
8f1f1ba
Run the `cmake.yml` action on macOS too.
dimacurrentai Jan 2, 2024
ea55bfc
Renamed the action.
dimacurrentai Jan 2, 2024
cd1bcb2
Made it run on each PR commit.
dimacurrentai Jan 2, 2024
d068bac
More Action-friendly output.
dimacurrentai Jan 2, 2024
35d44d5
Added a `TODO`.
dimacurrentai Jan 2, 2024
b0a21ec
Added the test for `lib_*.{cc,h}`.
dimacurrentai Jan 2, 2024
92786db
Added support for `.so` targets as well.
dimacurrentai Jan 2, 2024
53ef2ff
Using single quotes instead of tick quotes in `cat <<EOF` because macOS.
dimacurrentai Jan 2, 2024
e836870
macOS experiments on Github ...
dimacurrentai Jan 2, 2024
c08e357
... more macOS experiments.
dimacurrentai Jan 2, 2024
f1e9391
... and even more ...
dimacurrentai Jan 2, 2024
3141d35
Looks like macOS is defeated, tests pass.
dimacurrentai Jan 2, 2024
faf8a33
Work in progress for cross-platform `dlopen`.
dimacurrentai Jan 2, 2024
2962d0b
Fixed it!
dimacurrentai Jan 2, 2024
d719640
Final `cmake/README.md tweaks.
dimacurrentai Jan 2, 2024
05e56a4
Minor.
dimacurrentai Jan 2, 2024
ab8fb2e
Minor II.
dimacurrentai Jan 2, 2024
6e93509
Removed the `q/q/q` test dir, whoa.
dimacurrentai Jan 3, 2024
57208a4
Minor README tweak.
dimacurrentai Jan 3, 2024
cdbadb1
Made the test run dir truly nested.
dimacurrentai Jan 3, 2024
c52ecc6
The [hopefully] last minor tweak for this PR.
dimacurrentai Jan 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: cmake

on:
workflow_dispatch:
pull_request:
types: [opened, synchronize]

jobs:
try-cmake:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: git clone
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: run test
run: |
export TESTDIR="cmake_test_$(date +%s)/deeply/nested/dir"
echo "Running in ${TESTDIR}"
mkdir -p "${TESTDIR}"
cp cmake/Makefile "${TESTDIR}/"
cp cmake/CMakeLists.txt "${TESTDIR}/"
cp cmake/run-cmake-test.sh "${TESTDIR}/"
(cd "${TESTDIR}"; ./run-cmake-test.sh)
10 changes: 10 additions & 0 deletions bricks/system/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ class DynamicLibrary {
}
}

static DynamicLibrary CrossPlatform(const std::string library_file_name_without_extension) {
return DynamicLibrary(library_file_name_without_extension +
#ifdef CURRENT_APPLE
".dylib"
#else
".so"
#endif
);
}

virtual ~DynamicLibrary() {
if (lib_) {
::dlclose(lib_);
Expand Down
80 changes: 80 additions & 0 deletions cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# TODO(dkorolev): Consider intelligently re-running `cmake` from the `Makefile` if the set of source files has changed.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOTE(dkorolev), @mzhurovich, this was tested a long time ago in https://github.com/dkorolev/example_use_current_with_cmake, just cleaned up a bit and moved this into current.'


cmake_minimum_required(VERSION 3.14.1)

project(cmake_trivial_2023 C CXX)

set (CMAKE_CXX_STANDARD 17)
find_package(Threads REQUIRED)

# Settings for `googletest`. It builds faste without `gmock`.
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
option(BUILD_GMOCK OFF)

# The helper to clone a dependency, or use it from a sibling dir if available.
function(UseOrGitClone dep remote branch)
if(EXISTS "${CMAKE_SOURCE_DIR}/${dep}" AND IS_DIRECTORY "${CMAKE_SOURCE_DIR}/${dep}")
message(STATUS "Using `${dep}` from `CMAKE_SOURCE_DIR/${dep}'.")
add_subdirectory("${CMAKE_SOURCE_DIR}/${dep}" ${dep})
message(STATUS "Using `${dep}` from `CMAKE_SOURCE_DIR/${dep}': Configured.")
elseif(EXISTS "${CMAKE_SOURCE_DIR}/../${dep}" AND IS_DIRECTORY "${CMAKE_SOURCE_DIR}/../${dep}")
message(STATUS "Using `${dep}` from `CMAKE_SOURCE_DIR/../${dep}'.")
add_subdirectory("${CMAKE_SOURCE_DIR}/../${dep}" ${dep})
message(STATUS "Using `${dep}` from `CMAKE_SOURCE_DIR/../${dep}': Configured.")
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../${dep}" AND IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../${dep}")
message(STATUS "Using `${dep}` from `CMAKE_CURRENT_SOURCE_DIR/../${dep}'.")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../${dep}" ${dep})
message(STATUS "Using `${dep}` from `CMAKE_CURRENT_SOURCE_DIR/../${dep}': Configured.")
else()
message(STATUS "Cloning `${dep}` from `${remote}:${branch}` ...")
execute_process(OUTPUT_QUIET ERROR_QUIET COMMAND git clone --depth 1 -b ${branch} ${remote})
file(TOUCH .gitignore)
file(APPEND .gitignore "${dep}/\n")
add_subdirectory("${CMAKE_SOURCE_DIR}/${dep}" ${dep})
message(STATUS "Cloning `${dep}` from `${remote}:${branch}`: Configured.")
endif()
endfunction()

#UseOrGitClone(current https://github.com/c5t/current stable)
UseOrGitClone(current https://github.com/dimacurrentai/current cmake)
# TODO(dkorolev): Fix this once the PR is merged.

UseOrGitClone(googletest https://github.com/c5t/googletest v1.14)

# Declare shared libraries as shared library targets. Do not link them against anything external.
file(GLOB_RECURSE BINARY_SOURCE_FILES "src/dlib_*.cc")
foreach(SHARED_LIBRARY_SOURCE_FILE ${BINARY_SOURCE_FILES})
get_filename_component(SHARED_LIBRARY_TARGET_NAME "${SHARED_LIBRARY_SOURCE_FILE}" NAME_WE)
add_library(${SHARED_LIBRARY_TARGET_NAME} SHARED "${SHARED_LIBRARY_SOURCE_FILE}")
# TODO(dkorolev): Might be worth it to `grep` this `dlib_*.cc` source file for required library dependencies.
target_link_libraries(${SHARED_LIBRARY_TARGET_NAME} PRIVATE "${ALL_LIBRARIES}")
endforeach()

set(ALL_LIBRARIES "Threads::Threads" "C5T")
# Declare libraries as library targets. And add them into the `ALL_LIBRARIES` list.
file(GLOB_RECURSE LIBRARY_SOURCE_FILES "src/lib_*.cc")
foreach(LIBRARY_SOURCE_FILE ${LIBRARY_SOURCE_FILES})
get_filename_component(LIBRARY_TARGET_NAME "${LIBRARY_SOURCE_FILE}" NAME_WE)
add_library(${LIBRARY_TARGET_NAME} "${LIBRARY_SOURCE_FILE}")
list(APPEND ALL_LIBRARIES "${LIBRARY_TARGET_NAME}")
endforeach()

# Declare binaries as binary targets. And link them against all the libraries.
file(GLOB_RECURSE BINARY_SOURCE_FILES "src/*.cc")
foreach(BINARY_SOURCE_FILE ${BINARY_SOURCE_FILES})
get_filename_component(BINARY_TARGET_NAME "${BINARY_SOURCE_FILE}" NAME_WE)
if(NOT (BINARY_TARGET_NAME MATCHES "^lib_.*$" OR BINARY_TARGET_NAME MATCHES "^test_.*$" OR BINARY_TARGET_NAME MATCHES "^dlib_.*$"))
add_executable(${BINARY_TARGET_NAME} "${BINARY_SOURCE_FILE}")
target_link_libraries(${BINARY_TARGET_NAME} PRIVATE "${ALL_LIBRARIES}")
endif()
endforeach()

# Declare tests as test targets. And link them against all the libraries.
enable_testing()
file(GLOB_RECURSE TEST_SOURCE_FILES "src/test_*.cc")
foreach(TEST_SOURCE_FILE ${TEST_SOURCE_FILES})
get_filename_component(TEST_TARGET_NAME "${TEST_SOURCE_FILE}" NAME_WE)
add_executable(${TEST_TARGET_NAME} "${TEST_SOURCE_FILE}")
target_link_libraries(${TEST_TARGET_NAME} PRIVATE gtest_main "${ALL_LIBRARIES}")
add_test(NAME ${TEST_TARGET_NAME} COMMAND ${TEST_TARGET_NAME})
endforeach()
60 changes: 60 additions & 0 deletions cmake/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# NOTE(dkorolev):
#
# Yes, I am well aware it is ugly to have a `Makefile` for a `cmake`-built project.
#
# However, there is quite some value.
# Please see the README of https://github.com/dimacurrentai/Current/tree/cmake/cmake for more details.
# TODO(dkorolev): Fix the repo & branch on the line above once merged.
#
# Besides, this `Makefiles` makes `:mak` in Vim work like a charm!

.PHONY: release debug release_dir debug_dir release_test debug_test test fmt clean

DEBUG_BUILD_DIR=$(shell echo "$${DEBUG_BUILD_DIR:-.current_debug}")
RELEASE_BUILD_DIR=$(shell echo "$${RELEASE_BUILD_DIR:-.current}")

OS=$(shell uname)
ifeq ($(OS),Darwin)
CORES=$(shell sysctl -n hw.physicalcpu)
else
CORES=$(shell nproc)
endif

CLANG_FORMAT=$(shell echo "$${CLANG_FORMAT:-clang-format}")

release: release_dir CMakeLists.txt
@MAKEFLAGS=--no-print-directory cmake --build "${RELEASE_BUILD_DIR}" -j ${CORES}

release_dir: ${RELEASE_BUILD_DIR}
@grep "^${RELEASE_BUILD_DIR}/$$" .gitignore >/dev/null || echo "${RELEASE_BUILD_DIR}/" >>.gitignore

${RELEASE_BUILD_DIR}: CMakeLists.txt src
@cmake -DCMAKE_BUILD_TYPE=Release -B "${RELEASE_BUILD_DIR}" .

test: release
@(cd "${RELEASE_BUILD_DIR}"; make test)

debug: debug_dir CMakeLists.txt
@MAKEFLAGS=--no-print-directory cmake --build "${DEBUG_BUILD_DIR}" -j ${CORES}

debug_dir: ${DEBUG_BUILD_DIR}
@grep "^${DEBUG_BUILD_DIR}/$$" .gitignore >/dev/null || echo "${DEBUG_BUILD_DIR}/" >>.gitignore

${DEBUG_BUILD_DIR}: CMakeLists.txt src
@cmake -B "${DEBUG_BUILD_DIR}" .

debug_test: debug
@(cd "${DEBUG_BUILD_DIR}"; make test)

test: release_test

fmt:
${CLANG_FORMAT} -i src/*.cc src/*.h

# TODO(dkorolev): Use the proper repo & branch name.
CMakeLists.txt:
@curl -s https://raw.githubusercontent.com/dimacurrentai/Current/cmake/cmake/CMakeLists.txt >$@
# TODO(dkorolev): Fix the repo & branch on the line above once merged.

clean:
rm -rf "${DEBUG_BUILD_DIR}" "${RELEASE_BUILD_DIR}"
27 changes: 27 additions & 0 deletions cmake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `C5T/current/cmake/`

This directory contains the `CMakeLists.txt`-based setup for a user project that needs Current.

This `CMakeLists.txt` is accompanied by a `Makefile`. Together, they:

* Make the user depend only on the `Makefile`.
* I.e., it is enough to obtain the `Makefile` from this dir, the rest is magic.
* This `Makefile` will `curl` the `CMakeLists.txt` file as needed.
* Builds the code from `src/`.
* Every `src/*.cc` becomes a binary.
* Every `src/lib_*.cc` becomes a static library.
* Every `src/test_*.cc` becomes a test target.
* Every `src/dlib_*.cc` becomes a shared `.{so|dylib}` library.
* All static libraries are linked to all binaries and tests for this simple setup.
* Linking static libraries to shared libraries is still a `TODO(dkorolev)`.
* Defines many useful `make` targets:
* `clean`, `debug`, `release`, `test`, `release_test`, `clean`, `fmt`, etc.
* Uses `current` and `googletest` from `..` or `../..` if they exist.
* Clones `current` and `googletest` if needed.
* Adds them into `.gitignore` if they are cloned into the local dir.

The `test/` directory in this repo illustrates what it takes to use this `Makefile` + `CMakeLists.txt`.

Please refer to [`run-cmake-test.sh`](https://github.com/dimacurrentai/Current/blob/cmake/cmake/run-cmake-test.sh) for a comprehensive end-to-end test for the above.

TODO(dkorolev): Fix the path once the the PR is merged in.
Loading