diff --git a/.gitattributes b/.gitattributes index a13b89f..f04a53f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,6 +2,8 @@ # Linguist # ========================== docs/** linguist-documentation +examples/** linguist-language=ignore +scripts/** linguist-language=ignore tests/** linguist-language=ignore # ========================== diff --git a/CHANGELOG.md b/CHANGELOG.md index 3224850..b968776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [6.3.0] - 2025-11-18 -- ... +### Added +- Support of header-only and source libraries via INTERFACE +- `add_component_set` to register a group of components without creating local targets. +- Automatic `::` aliases for components to simplify linking from parent projects that do not use abcmake. ## [6.2.0] - 2025-09-17 diff --git a/CMakeLists.txt b/CMakeLists.txt index 72aa578..ba1ba44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,49 +16,22 @@ set(ABCMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/cmake/abcmake" # Install root files install(FILES src/ab.cmake + src/abcmakeConfig.cmake + src/abcmakeConfigVersion.cmake src/version.cmake DESTINATION ${ABCMAKE_INSTALL_CMAKEDIR} ) -# Install abcmake subdirectory files install(FILES + src/abcmake/_abcmake_add_project.cmake src/abcmake/_abcmake_log.cmake src/abcmake/_abcmake_property.cmake - src/abcmake/_abcmake_add_project.cmake src/abcmake/add_component.cmake src/abcmake/register_components.cmake src/abcmake/target_link_components.cmake - LICENSE DESTINATION ${ABCMAKE_INSTALL_CMAKEDIR}/abcmake ) -# Generate the config file that includes all the exports -configure_package_config_file( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/abcmakeConfig.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/abcmakeConfig.cmake" - INSTALL_DESTINATION ${ABCMAKE_INSTALL_CMAKEDIR} - PATH_VARS ABCMAKE_INSTALL_CMAKEDIR -) - -# Generate the version file for the config file -write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/abcmakeConfigVersion.cmake" - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion -) - -# Install the generated config files -install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/abcmakeConfig.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/abcmakeConfigVersion.cmake" - DESTINATION ${ABCMAKE_INSTALL_CMAKEDIR} -) - -# Install LICENSE to abcmake subdirectory -install(FILES - LICENSE - DESTINATION ${ABCMAKE_INSTALL_CMAKEDIR}/abcmake -) # Copy installer and docs to package root (CMAKE_INSTALL_PREFIX) install(CODE " diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..509f4a3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,212 @@ +# Contributing to abcmake + +Thank you for considering contributing to abcmake! This document provides guidelines and instructions for contributing. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [How Can I Contribute?](#how-can-i-contribute) +- [Development Setup](#development-setup) +- [Testing](#testing) +- [Pull Request Process](#pull-request-process) +- [Coding Standards](#coding-standards) +- [Documentation](#documentation) + +## Code of Conduct + +This project adheres to a code of conduct that encourages respectful collaboration. By participating, you are expected to uphold this standard. Please report unacceptable behavior to the project maintainers. + +## How Can I Contribute? + +### Reporting Bugs + +Before creating bug reports, please check the existing issues to avoid duplicates. When creating a bug report, include: + +- **Clear descriptive title** +- **Detailed steps to reproduce** the issue +- **Expected behavior** vs **actual behavior** +- **Environment details** (OS, CMake version, compiler) +- **Minimal reproducible example** if possible +- **Error messages** or logs + +### Suggesting Enhancements + +Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion: + +- **Use a clear and descriptive title** +- **Provide detailed description** of the proposed feature +- **Explain why this enhancement would be useful** +- **Include code examples** if applicable + +### Pull Requests + +We actively welcome your pull requests! Here's how to contribute code: + +1. **Fork the repository** and create your branch from `main` +2. **Make your changes** with clear, logical commits +3. **Add or update tests** for your changes +4. **Update documentation** to reflect your changes +5. **Ensure the test suite passes** +6. **Submit your pull request** + +## Development Setup + +### Prerequisites + +- CMake >= 3.15 +- Python 3.x (for running tests) +- Git + +### Setting Up Your Development Environment + +1. **Fork and clone the repository**: + ```bash + git clone https://github.com/YOUR_USERNAME/abcmake.git + cd abcmake + ``` + +2. **Create a feature branch**: + ```bash + git checkout -b feature/your-feature-name + ``` + +3. **Install abcmake locally** for testing: + ```bash + cmake -B build + cmake --install build --prefix ~/.local + ``` + +## Testing + +abcmake uses Python-based integration tests to ensure reliability. + +### Running Tests + +From the project root: + +```bash +cd tests +python -m unittest discover -v +``` + +### Running Specific Tests + +```bash +python -m unittest tests.test_basic -v +``` + +### Writing Tests + +When adding new features: + +1. Add test cases in the `tests/` directory +2. Follow the existing test structure +3. Ensure tests are self-contained +4. Test both success and failure scenarios + +### Test Coverage + +We aim for comprehensive test coverage. Please ensure: + +- New features have corresponding tests +- Bug fixes include regression tests +- Edge cases are covered + +## Pull Request Process + +1. **Update CHANGELOG.md** under the `Unreleased` section with your changes +2. **Update documentation** as needed +3. **Ensure all tests pass** before submitting +4. **Keep PRs focused** - one feature/fix per PR +5. **Write clear commit messages** following these guidelines: + - Use present tense ("Add feature" not "Added feature") + - Use imperative mood ("Move cursor to..." not "Moves cursor to...") + - Limit first line to 72 characters + - Reference issues and pull requests liberally + +### PR Review Process + +- Maintainers will review your PR as soon as possible +- Address any requested changes +- Once approved, a maintainer will merge your PR +- Your contribution will be included in the next release + +## Coding Standards + +### CMake Code Style + +- **Indentation**: 4 spaces (no tabs) +- **Function names**: `snake_case` +- **Variable names**: `UPPER_SNAKE_CASE` for cache variables, `snake_case` for local +- **Comments**: Use `#` for single-line comments, keep them clear and concise + +### Example + +```cmake +# Good +function(my_helper_function target_name) + set(LOCAL_VAR "value") + set(CACHE_VAR "value" CACHE STRING "Description") +endfunction() + +# Avoid +function(MyHelperFunction targetName) + set(localVar "value") +endfunction() +``` + +### Best Practices + +- Keep functions focused and single-purpose +- Use meaningful variable names +- Add comments for complex logic +- Avoid global state when possible +- Handle error cases explicitly + +## Documentation + +### Documentation Requirements + +When contributing, please update relevant documentation: + +- **README.md** - For user-facing changes +- **docs/api.md** - For API changes +- **docs/examples.md** - For new usage patterns +- **Code comments** - For complex implementations + +### Documentation Style + +- Write clear, concise documentation +- Include code examples where appropriate +- Use proper Markdown formatting +- Keep language simple and accessible +- Test code examples to ensure they work + +## Release Process + +Releases are handled by maintainers: + +1. Update version numbers +2. Update CHANGELOG.md +3. Create GitHub release +4. Generate single-file distribution (`ab.cmake`) +5. Update documentation as needed + +## Questions? + +If you have questions about contributing: + +- Check existing documentation +- Search closed issues for similar questions +- Open a new issue with your question +- Tag it appropriately + +## Recognition + +Contributors are recognized in: + +- Git history +- Release notes +- GitHub contributors page + +Thank you for making abcmake better! diff --git a/README.md b/README.md index 87add8c..b7a0fcf 100644 --- a/README.md +++ b/README.md @@ -1,211 +1,348 @@ -
+
![logo](docs/README/header.drawio.svg) -Simple, component‑first CMake helper for small & medium C/C++ projects. +**Simple, component‑first CMake helper for small & medium C/C++ projects** [![GitHub Release](https://img.shields.io/github/v/release/an-dr/abcmake?label=latest%20release)](https://github.com/an-dr/abcmake/releases) [![Build Test](https://github.com/an-dr/abcmake/actions/workflows/test.yml/badge.svg)](https://github.com/an-dr/abcmake/actions/workflows/test.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![CMake 3.15+](https://img.shields.io/badge/CMake-3.15+-064F8C?logo=cmake)](https://cmake.org/) + +[Quick Start](docs/quick-start.md) • +[Documentation](#documentation) • +[Examples](docs/examples.md) • +[API Reference](docs/api.md)
-`abcmake` (Andrei's Build CMake subsystem) lets you structure a codebase as independent *components* with minimal boilerplate: drop folders under `components/`, call one function, get targets, includes, and linking done automatically. +--- + +## Overview + +**abcmake** (Andrei's Build CMake subsystem) lets you structure codebases as independent *components* with minimal boilerplate. Drop folders under `components/`, call one function, and get automatic target creation, include paths, and linking. + +### Why abcmake? + + + + + + +
+ +#### Traditional CMake + +```cmake +file(GLOB_RECURSE SOURCES "src/*.cpp") +add_library(mylib ${SOURCES}) +target_include_directories(mylib PUBLIC include) + +add_subdirectory(components/comp_a) +target_link_libraries(mylib PUBLIC comp_a) + +add_subdirectory(components/comp_b) +target_link_libraries(mylib PUBLIC comp_b) + +# Repeat for every component... +``` + + + +#### With abcmake + +```cmake +include(ab.cmake) + +add_component(mylib) + +# Done! All components auto-discovered +# and linked, includes configured +``` -## Why abcmake? +
-| Problem | What abcmake Gives You | -| --------------------------------------------- | -------------------------------------------------------- | -| Repeating `add_library` + globbing everywhere | Single `add_main_component()` + auto component discovery | -| Hard to reuse internal modules | Component folders become portable units | -| Tedious dependency wiring | `target_link_components()` + optional registry by name | -| Vendored CMake packages cumbersome | Auto‑detect `*Config.cmake` in `components/` and link | -| Monolithic CMakeLists.txt growth | Split naturally by component directory | +### Key Benefits + +| Problem | abcmake Solution | +|---------|-----------------| +| 🔁 Repeating `add_library` + globbing everywhere | Single `add_component()` with auto-discovery | +| 📦 Hard to reuse internal modules | Component folders become portable units | +| 🔗 Tedious dependency wiring | Automatic linking + optional registry | +| 📚 Vendored CMake packages cumbersome | Auto-detect & link `*Config.cmake` packages | +| 📈 Monolithic CMakeLists.txt growth | Natural split by component directory | ## Features -- Zero external Python/CMake dependency beyond stock CMake (>= 3.15). -- Conventional project layout with overridable source/include directories. -- Automatic recursive discovery & linking of nested components. -- Registry for linking components by *name* rather than path. -- Auto-detection of vendored CMake packages (`*Config.cmake`). -- Generates `compile_commands.json` by default. -- Install step for each built target near the build dir. -- Single-file distributable (`ab.cmake`) published per GitHub Release. - -## Table Of Contents - -- [Why abcmake?](#why-abcmake) -- [Features](#features) -- [Table Of Contents](#table-of-contents) -- [Quick Start](#quick-start) - - [Install](#install) - - [Use](#use) -- [Concepts](#concepts) -- [Public API](#public-api) - - [`add_main_component( [INCLUDE_DIR ...] [SOURCE_DIR ...])`](#add_main_componentname-include_dir--source_dir-) - - [`add_component( [SHARED] [INCLUDE_DIR ...] [SOURCE_DIR ...])`](#add_componentname-shared-include_dir--source_dir-) - - [`register_components( ...)`](#register_componentspath-) - - [`target_link_components( [PATH ...] [NAME ...])`](#target_link_componentstarget-path-path--name-comp-) - - [Auto Package Detection](#auto-package-detection) -- [Advanced Usage](#advanced-usage) -- [Limitations](#limitations) -- [Contributing](#contributing) -- [Changelog](#changelog) -- [License](#license) +- ✨ **Zero dependencies** - Pure CMake 3.15+, no Python or external tools +- 🎯 **Convention over configuration** - Sensible defaults, override when needed +- 🔍 **Automatic discovery** - Recursive component detection and linking +- 📝 **Component registry** - Link by name instead of path +- 🧩 **Component sets** - Bulk registration without building +- 🔌 **Package auto-detection** - Vendored `*Config.cmake` packages just work +- 🏷️ **Automatic aliases** - `::` for parent project compatibility +- 🛠️ **IDE support** - Generates `compile_commands.json` by default +- 📦 **Single-file distribution** - Just download `ab.cmake` ## Quick Start -### Install +### 1. Install -**Project Scope**. Download a single-file distribution and put near your CMakeLists.txt: +#### Option A: Project-Scoped (Recommended) -- +Download [`ab.cmake`](https://github.com/an-dr/abcmake/releases/latest/download/ab.cmake) to your project root. -**User Scope**. Clone the repo and install it in your system: +#### Option B: User/System-Wide ```bash -git clone https://github.com/you/abcmake.git +git clone https://github.com/an-dr/abcmake.git cd abcmake cmake -B build cmake --install build --prefix ~/.local ``` -For Windows: +📖 [Detailed Installation Guide](docs/installation.md) + +### 2. Create Your Project -- Use `$env:LOCALAPPDATA\CMake` instead of `~/.local` also append the path: +**CMakeLists.txt:** ```cmake -list(APPEND CMAKE_PREFIX_PATH "$ENV{LOCALAPPDATA}/CMake") -find_package(abcmake REQUIRED) +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(ab.cmake) + +add_main_component(${PROJECT_NAME}) +``` + +**src/main.cpp:** + +```cpp +#include + +int main() { + std::cout << "Hello, abcmake!" << std::endl; + return 0; +} ``` -**System-wide Scope**. Change prefix to `/usr/local` (Linux) or `C:\Program Files\CMake` (Windows), run with elevated privileges +**Build:** ```bash -git clone https://github.com/you/abcmake.git -cd abcmake cmake -B build -sudo cmake --install build --prefix /usr/local +cmake --build build +./build/MyApp ``` -### Use +### 3. Add a Component -Minimal root `CMakeLists.txt`: +**components/greeter/CMakeLists.txt:** ```cmake cmake_minimum_required(VERSION 3.15) -project(HelloWorld) +project(greeter) -find_package(abcmake REQUIRED) -# or include(ab.cmake) for single-file distribution +include(../../ab.cmake) -add_main_component(${PROJECT_NAME}) +add_component(${PROJECT_NAME}) ``` -Project layout: +**components/greeter/include/greeter/greeter.hpp:** -```text -project/ - CMakeLists.txt - ab.cmake - src/ - main.cpp - include/ (optional public headers) - components/ - mylib/ - src/ ... - include/ ... - CMakeLists.txt (uses abcmake + add_component()) -``` +```cpp +#pragma once +#include -Add a component (`components/mylib/CMakeLists.txt`): +std::string greet(const std::string& name); +``` -```cmake -cmake_minimum_required(VERSION 3.15) -project(mylib) +**components/greeter/src/greeter.cpp:** -find_package(abcmake REQUIRED) +```cpp +#include "greeter/greeter.hpp" -add_component(${PROJECT_NAME}) +std::string greet(const std::string& name) { + return "Hello, " + name + "!"; +} ``` -## Concepts +That's it! The component is automatically discovered, built, and linked. + +📖 [Full Quick Start Guide](docs/quick-start.md) + +## Documentation + +### Getting Started + +- 🚀 [Quick Start Guide](docs/quick-start.md) - Get up and running in 5 minutes +- 📦 [Installation Guide](docs/installation.md) - Detailed installation options +- 💡 [Core Concepts](docs/concepts.md) - Understand the component model -| Term | Meaning | -| -------------- | ------------------------------------------------------------------------------------------ | -| Component | A folder with its own `CMakeLists.txt` that calls `add_component` or `add_main_component`. | -| Main component | The top-level executable (or library) defined with `add_main_component`. | -| Registry | A global list of discovered components (added via `register_components`). | -| Auto package | A directory under `components/` containing `*Config.cmake` -> treated as a CMake package. | +### References -## Public API +- 📚 [API Reference](docs/api.md) - Complete function documentation +- 🔧 [Examples](docs/examples.md) - Real-world usage patterns +- 📝 [Changelog](CHANGELOG.md) - Version history and changes -### `add_main_component( [INCLUDE_DIR ...] [SOURCE_DIR ...])` +### Contributing -Creates an executable (or top-level library) and automatically: +- 🤝 [Contributing Guide](CONTRIBUTING.md) - How to contribute +- 🐛 [Issue Tracker](https://github.com/an-dr/abcmake/issues) - Report bugs or request features -- Adds sources from provided `SOURCE_DIR` list (default `src`). -- Adds include dirs (default `include` if exists). -- Discovers & links nested components in `components/`. +## Project Structure -### `add_component( [SHARED] [INCLUDE_DIR ...] [SOURCE_DIR ...])` +```text +my_project/ +├── CMakeLists.txt # include(ab.cmake) + add_main_component() +├── ab.cmake # Single-file abcmake distribution +├── src/ # Main application sources +│ └── main.cpp +├── include/ # (Optional) Public headers +│ └── myapp/ +│ └── config.hpp +└── components/ # Auto-discovered components + ├── component_a/ + │ ├── CMakeLists.txt # add_component(component_a) + │ ├── include/component_a/ + │ │ └── api.hpp + │ └── src/ + │ └── impl.cpp + └── component_b/ + ├── CMakeLists.txt + └── ... +``` -Defines a static (default) or shared library component with the same discovery & inclusion mechanics. +## API at a Glance -### `register_components( ...)` +```cmake +# Create main executable with auto-discovery +add_main_component( [SOURCE_DIR ...] [INCLUDE_DIR ...]) -Registers components so you can later link by name instead of path. +# Create library component (static by default) +add_component( [SHARED|INTERFACE] [SOURCE_DIR ...] [INCLUDE_DIR ...]) -### `target_link_components( [PATH ...] [NAME ...])` +# Register components for name-based linking +register_components(...) -Links components to a target via explicit paths and/or previously registered names. +# Link components by path or name +target_link_components( [PATH ...] [NAME ...]) -#### Auto Package Detection +# Bulk register components without building +add_component_set([PATH ] [COMPONENTS ...] [REGISTER_ALL]) +``` -Any directory in `components/` containing a `*Config.cmake` is probed with `find_package( CONFIG PATHS NO_DEFAULT_PATH QUIET)`. Targets `` or `::` are auto-linked if present. +📖 [Complete API Documentation](docs/api.md) -## Advanced Usage +## Examples -Linking multiple sources & includes: +### Multiple Source Directories ```cmake -add_component(core \ - INCLUDE_DIR include public_api \ - SOURCE_DIR src generated) +add_component(core + SOURCE_DIR src generated + INCLUDE_DIR include public_api) ``` -Mix path + name linking: +### Component Registry ```cmake -register_components(${CMAKE_CURRENT_LIST_DIR}/libs/math) -target_link_components(app \ - PATH ${CMAKE_CURRENT_LIST_DIR}/libs/io \ - NAME math) +# Register once +register_components( + ${CMAKE_CURRENT_LIST_DIR}/components/logger + ${CMAKE_CURRENT_LIST_DIR}/components/config) + +# Link by name anywhere +target_link_components(app NAME logger config) ``` -Custom project layout (no `src/`): +### Shared Library ```cmake -add_main_component(App SOURCE_DIR source INCLUDE_DIR include) +add_component(myplugin SHARED) ``` +### Header-Only Library + +```cmake +add_component(templates INTERFACE) +``` + +📖 [More Examples](docs/examples.md) + +## Requirements + +- **CMake** 3.15 or higher +- **C/C++ compiler** (GCC, Clang, MSVC, etc.) +- **Platforms**: Linux, macOS, Windows + +## FAQ + +
+Can I use abcmake with existing CMake projects? + +Yes! abcmake components create standard CMake targets with `::` aliases. Parent projects can use them with `add_subdirectory()` whether or not they use abcmake. +
+ +
+Does abcmake support header-only libraries? + +Absolutely! Use `add_component( INTERFACE)` for header-only components. +
+ +
+How do I integrate third-party libraries? + +Drop vendored libraries with `*Config.cmake` into `components/` - they're auto-detected and linked. Or use standard CMake `find_package()` and `target_link_libraries()`. +
+ +
+Can components have sub-components? + +Yes! Components can have their own `components/` directory with nested components. Discovery is recursive. +
+ +
+Is abcmake suitable for large projects? + +abcmake is optimized for small to medium projects. Very large monorepos may prefer native CMake patterns for fine-grained control. +
+ ## Limitations -- One logical component per `CMakeLists.txt` (keep them focused). -- Designed for small/medium modular trees; very large monorepos may prefer native CMake patterns. +- 📋 One logical component per `CMakeLists.txt` (keeps components focused) +- 🎯 Best suited for small/medium projects (large monorepos may need more control) +- 🔧 Convention-based (less flexibility than raw CMake for complex scenarios) ## Contributing -1. Fork & branch. -2. Run the test suite: `python -m unittest discover -v` (from `tests/`). -3. Keep PRs focused & update the **Unreleased** section in `CHANGELOG.md`. +We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for: -## Changelog +- 🐛 Reporting bugs +- 💡 Suggesting features +- 🔧 Development setup +- ✅ Running tests +- 📝 Documentation improvements -See [CHANGELOG.md](CHANGELOG.md) for structured history. +**Testing:** + +```bash +cd tests +python -m unittest discover -v +``` ## License -MIT License © Andrei Gramakov. +MIT License © [Andrei Gramakov](https://github.com/an-dr) See [LICENSE](LICENSE) for details. + +--- + +
+ +**[⬆ Back to Top](#overview)** + +Made with ❤️ by the abcmake community + +
diff --git a/cmake/abcmakeConfig.cmake.in b/cmake/abcmakeConfig.cmake.in deleted file mode 100644 index bcd1076..0000000 --- a/cmake/abcmakeConfig.cmake.in +++ /dev/null @@ -1,28 +0,0 @@ -@PACKAGE_INIT@ - -# abcmake Package Configuration File -# -# This file sets up the abcmake build system for use in other projects. -# After installation, use it with: -# find_package(abcmake REQUIRED) -# include(${abcmake_DIR}/ab.cmake) - -set(ABCMAKE_VERSION "@PROJECT_VERSION@") - -# Compute the installation prefix relative to this file -get_filename_component(ABCMAKE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) - -# Set ABCMAKE_PATH to the installation directory -set(ABCMAKE_PATH "${ABCMAKE_CMAKE_DIR}") - -# Make abcmake functions available -include("${ABCMAKE_CMAKE_DIR}/ab.cmake") - -check_required_components(abcmake) - -# Provide helpful variables for users -set(abcmake_FOUND TRUE) -set(abcmake_VERSION "${ABCMAKE_VERSION}") -set(abcmake_DIR "${ABCMAKE_CMAKE_DIR}") - -message(STATUS "Found abcmake: ${ABCMAKE_VERSION} (${ABCMAKE_CMAKE_DIR})") diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..a0de0f4 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,106 @@ +# API Reference + +- [API Reference](#api-reference) + - [add\_main\_component](#add_main_component) + - [add\_component](#add_component) + - [add\_component\_set](#add_component_set) + - [register\_components](#register_components) + - [target\_link\_components](#target_link_components) + +## add_main_component + +```cmake +add_main_component( [INCLUDE_DIR ...] [SOURCE_DIR ...]) +``` + +Creates the top-level executable (or library) and discovers nested components under `components/`. + +- Defaults: `SOURCE_DIR src`, `INCLUDE_DIR include` (if it exists). +- Includes each `SOURCE_DIR` and adds include paths; links discovered components automatically. + +Example: + +```cmake +add_main_component(my_app + INCLUDE_DIR include public_api + SOURCE_DIR src core) +``` + +## add_component + +```cmake +add_component( [SHARED|INTERFACE] [INCLUDE_DIR ...] [SOURCE_DIR ...]) +``` + +Defines a library component with the same discovery/linking as `add_main_component`. + +- Static by default; `SHARED` for shared libs; `INTERFACE` for header-only/consumer-built sources. +- Adds include dirs and sources, then installs the target (non-interface). + +Example: + +```cmake +add_component(driver SHARED SOURCE_DIR drivers) +add_component(utils INTERFACE) # header-only +``` + +## add_component_set + +```cmake +add_component_set([PATH ] [COMPONENTS ...] [REGISTER_ALL]) +``` + +Registers a group of components without creating a local target—useful for plugin/dependency bundles. + +- `PATH` defaults to the configured components dir. +- `COMPONENTS` lists subdirectories to register; `REGISTER_ALL` sweeps all subdirs under `PATH`. + +Example: + +```cmake +add_component_set(REGISTER_ALL) # register every subdir under components + +add_component_set( + PATH third_party + COMPONENTS fmt spdlog) # register specific subdirs third_party +``` + +## register_components + +```cmake +register_components( ...) +``` + +Adds components to the global registry so they can be linked by name. + +- Use before `target_link_components(NAME ...)`. + +Example: + +```cmake +register_components( + ${CMAKE_CURRENT_LIST_DIR}/libs + ${CMAKE_CURRENT_LIST_DIR}/3rd_party) +``` + +## target_link_components + +```cmake +target_link_components( [PATH ...] [NAME ...]) +``` + +Links dependencies into `` by path and/or registered name. + +- Path entries look for abcmake components or raw CMake packages (`*Config.cmake`). +- Name entries resolve via the registry (from `register_components`). + +Example: + +```cmake +target_link_components( + app + PATH ${CMAKE_CURRENT_LIST_DIR}/vendor + ${CMAKE_CURRENT_LIST_DIR}/libs + NAME logger + crypto) +``` diff --git a/docs/concepts.md b/docs/concepts.md new file mode 100644 index 0000000..e45af3f --- /dev/null +++ b/docs/concepts.md @@ -0,0 +1,587 @@ +# Core Concepts + +This guide explains the fundamental concepts and design philosophy behind abcmake. + +## Table of Contents + +- [Philosophy](#philosophy) +- [Component Model](#component-model) +- [Component Discovery](#component-discovery) +- [The Component Registry](#the-component-registry) +- [Auto Package Detection](#auto-package-detection) +- [Directory Conventions](#directory-conventions) +- [Target Types](#target-types) +- [Linking Model](#linking-model) +- [Component Sets](#component-sets) +- [Best Practices](#best-practices) + +## Philosophy + +abcmake is built on these core principles: + +### 1. Convention Over Configuration + +Rather than manually specifying every detail, abcmake uses sensible defaults: + +- Sources in `src/` are automatically globbed +- Headers in `include/` are automatically exposed +- Components in `components/` are automatically discovered +- Linking happens automatically when possible + +### 2. Component-First Architecture + +Everything is a component. This promotes: + +- **Modularity** - Clear boundaries between code modules +- **Reusability** - Components can be easily moved between projects +- **Testability** - Components can be tested in isolation +- **Maintainability** - Changes are localized to component boundaries + +### 3. Minimal Boilerplate + +Traditional CMake requires repetitive patterns. abcmake reduces this: + +**Traditional CMake:** +```cmake +file(GLOB_RECURSE SOURCES "src/*.cpp") +file(GLOB_RECURSE HEADERS "include/*.hpp") +add_library(mylib ${SOURCES} ${HEADERS}) +target_include_directories(mylib PUBLIC include) +add_subdirectory(components/component_a) +target_link_libraries(mylib PUBLIC component_a) +add_subdirectory(components/component_b) +target_link_libraries(mylib PUBLIC component_b) +# ... repeat for every component +``` + +**With abcmake:** +```cmake +add_component(mylib) +# Done! All discovery and linking automatic +``` + +### 4. Zero External Dependencies + +abcmake is pure CMake - no Python scripts, no external tools. Just include one file and go. + +## Component Model + +### What is a Component? + +A **component** is a self-contained unit of code with: + +- Its own directory +- Its own `CMakeLists.txt` +- Clear public interface (headers) +- Implementation (sources) +- Optional dependencies (other components) + +### Component Anatomy + +``` +component_name/ +├── CMakeLists.txt # Build configuration +├── include/ # Public headers (API) +│ └── component_name/ # Namespaced headers +│ └── api.hpp +├── src/ # Private implementation +│ ├── impl.cpp +│ └── internal.hpp # Private headers +└── components/ # Optional sub-components + └── subcomponent/ +``` + +### Component Types + +#### 1. Main Component + +The top-level executable or library of your project. + +```cmake +add_main_component(MyApp) +``` + +Properties: +- Typically an executable +- Auto-discovers nested components +- Root of dependency tree + +#### 2. Library Component (Static) + +Standard library component, linked statically. + +```cmake +add_component(mylib) +``` + +Properties: +- Created as static library by default +- Binary code compiled into consumer +- No runtime dependency + +#### 3. Shared Library Component + +Component compiled as shared/dynamic library. + +```cmake +add_component(mylib SHARED) +``` + +Properties: +- Created as shared library (.so/.dll/.dylib) +- Loaded at runtime +- Can be updated independently +- Requires symbol visibility management + +#### 4. Interface Component (Header-Only) + +Component with no compiled code. + +```cmake +add_component(mylib INTERFACE) +``` + +Properties: +- Headers only, no .cpp files needed +- Template-heavy code +- Zero runtime overhead +- Consumer compiles the implementation + +## Component Discovery + +### Automatic Discovery + +When you call `add_main_component()` or `add_component()`, abcmake: + +1. **Searches for `components/` directory** +2. **Finds all subdirectories** with a `CMakeLists.txt` +3. **Automatically adds** each as a subdirectory +4. **Links discovered components** to the parent + +``` +project/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + ├── comp_a/ # Auto-discovered ✓ + │ └── CMakeLists.txt + ├── comp_b/ # Auto-discovered ✓ + │ └── CMakeLists.txt + └── doc_folder/ # No CMakeLists.txt - ignored +``` + +### Discovery Rules + +Components are discovered if: + +- Located under `components/` directory +- Directory contains `CMakeLists.txt` +- CMakeLists.txt calls `add_component()` or `add_main_component()` + +### Recursive Discovery + +Discovery works recursively - components can have sub-components: + +``` +components/ +└── core/ + ├── CMakeLists.txt # Defines 'core' component + └── components/ + ├── subsystem_a/ # Auto-discovered by 'core' + │ └── CMakeLists.txt + └── subsystem_b/ # Auto-discovered by 'core' + └── CMakeLists.txt +``` + +The `core` component automatically discovers and links `subsystem_a` and `subsystem_b`. + +## The Component Registry + +### What is the Registry? + +The registry is a global database of component names to paths, enabling **name-based linking**. + +### Why Use the Registry? + +Without registry (path-based linking): +```cmake +target_link_components(myapp + PATH ${CMAKE_CURRENT_SOURCE_DIR}/components/logger + ${CMAKE_CURRENT_SOURCE_DIR}/components/config + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/utils) +``` + +With registry (name-based linking): +```cmake +register_components( + ${CMAKE_CURRENT_SOURCE_DIR}/components/logger + ${CMAKE_CURRENT_SOURCE_DIR}/components/config + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/utils) + +target_link_components(myapp NAME logger config utils) +``` + +### Registering Components + +#### Manual Registration + +```cmake +register_components( ...) +``` + +Each path should point to a component directory (containing CMakeLists.txt with `add_component()`). + +#### Bulk Registration with Component Sets + +```cmake +# Register all components in a directory +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party + REGISTER_ALL) + +# Register specific components +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs + COMPONENTS math utils crypto) +``` + +### Registry Scope + +The registry is **global** within a CMake project: + +- Registered once, available everywhere +- Accessible from any CMakeLists.txt +- Persists across subdirectories + +## Auto Package Detection + +### What is Auto Package Detection? + +abcmake automatically detects and integrates vendored CMake packages. + +### How It Works + +Any directory under `components/` containing a `*Config.cmake` file is treated as a CMake package: + +``` +components/ +└── fmt/ + ├── fmtConfig.cmake # Detected as package! + ├── include/ + └── src/ +``` + +abcmake automatically: +1. Calls `find_package(fmt CONFIG PATHS components/fmt NO_DEFAULT_PATH)` +2. Links the target (tries `fmt` and `fmt::fmt`) +3. Makes it available to parent component + +### Benefits + +- Drop vendored libraries into `components/` +- Zero configuration needed +- Works with FetchContent +- Standard CMake package integration + +### Example + +**Project structure:** +``` +project/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + └── spdlog/ # Contains spdlogConfig.cmake +``` + +**CMakeLists.txt:** +```cmake +include(ab.cmake) +add_main_component(MyApp) +# spdlog automatically found and linked! +``` + +**main.cpp:** +```cpp +#include + +int main() { + spdlog::info("Hello from auto-detected spdlog!"); +} +``` + +## Directory Conventions + +### Standard Layout + +``` +component/ +├── CMakeLists.txt +├── src/ # Source files (.cpp, .c) +├── include/ # Public headers (.hpp, .h) +└── components/ # Sub-components +``` + +### Overriding Defaults + +Customize with `SOURCE_DIR` and `INCLUDE_DIR`: + +```cmake +add_component(mylib + SOURCE_DIR source implementation + INCLUDE_DIR headers public_api) +``` + +This searches: +- `source/` and `implementation/` for sources +- `headers/` and `public_api/` for headers + +### Include Path Namespacing + +**Best practice:** Namespace public headers: + +``` +include/ +└── mycomponent/ # Component name as namespace + ├── api.hpp + └── utils.hpp +``` + +Usage: +```cpp +#include "mycomponent/api.hpp" // Clear origin +``` + +Avoids: +```cpp +#include "api.hpp" // Which API? +``` + +## Target Types + +### Executable Targets + +Created by `add_main_component()`: + +```cmake +add_main_component(MyApp) +``` + +Produces: `MyApp` (or `MyApp.exe` on Windows) + +### Static Library Targets + +Created by `add_component()`: + +```cmake +add_component(mylib) +``` + +Produces: `libmylib.a` (or `mylib.lib` on Windows) + +### Shared Library Targets + +Created with `SHARED` keyword: + +```cmake +add_component(mylib SHARED) +``` + +Produces: `libmylib.so` / `mylib.dll` / `libmylib.dylib` + +### Interface Library Targets + +Created with `INTERFACE` keyword: + +```cmake +add_component(mylib INTERFACE) +``` + +Produces: No binary (header-only) + +## Linking Model + +### Automatic Linking + +Components discovered in `components/` are automatically linked: + +```cmake +add_main_component(MyApp) +# All components under components/ automatically linked +``` + +### Manual Linking by Path + +Link specific components explicitly: + +```cmake +target_link_components(myapp + PATH ${CMAKE_CURRENT_SOURCE_DIR}/components/logger + ${CMAKE_CURRENT_SOURCE_DIR}/components/config) +``` + +### Manual Linking by Name + +Link using registry names: + +```cmake +register_components(${CMAKE_CURRENT_SOURCE_DIR}/components/logger) + +target_link_components(myapp NAME logger) +``` + +### Mixed Linking + +Combine path and name linking: + +```cmake +target_link_components(myapp + PATH ${CMAKE_CURRENT_SOURCE_DIR}/vendor/lib_a + NAME logger config) +``` + +### Link Visibility + +All component links use `PRIVATE` visibility by default: + +- Component dependencies are implementation details +- Changes to components don't trigger rebuilds of downstream consumers +- Cleaner dependency graphs without transitive bloat +- INTERFACE libraries automatically use INTERFACE visibility + +## Component Sets + +### Purpose + +Component sets allow you to **register** components without **building** them locally. + +### Use Cases + +1. **Selective Linking** - Register many, link few +2. **Third-Party Libraries** - Register vendor components +3. **Plugin Collections** - Register all plugins for discovery + +### Example Scenario + +You have 10 third-party components, but each project only uses 2-3: + +```cmake +# Register all 10 components +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party + REGISTER_ALL) + +# Link only what this project needs +target_link_components(myapp NAME fmt spdlog) +``` + +Benefits: +- No need to modify third_party/CMakeLists.txt +- Only selected components are built +- Easy to add/remove dependencies + +## Best Practices + +### 1. One Component, One Purpose + +Keep components focused: + +``` +✓ Good: +components/ +├── json_parser/ +├── xml_parser/ +└── csv_parser/ + +✗ Avoid: +components/ +└── parsers/ # Too broad, many responsibilities +``` + +### 2. Use Include Namespacing + +Always namespace public headers: + +```cpp +// Good +#include "json_parser/parser.hpp" + +// Avoid +#include "parser.hpp" +``` + +### 3. Minimize Inter-Component Dependencies + +Components should be loosely coupled: + +``` +✓ Good dependency tree: +app → logger → (no dependencies) +app → database → logger + +✗ Avoid circular dependencies: +app → logger → database → logger // Circular! +``` + +### 4. Keep Components Portable + +Design components to work independently: + +- No hardcoded paths outside component +- Minimal assumptions about parent project +- Clear, documented dependencies + +### 5. Use Registry for Large Projects + +For projects with many components, use the registry: + +```cmake +# Register once at top level +add_component_set(PATH ${CMAKE_SOURCE_DIR}/libs REGISTER_ALL) + +# Link by name anywhere +target_link_components(app NAME crypto network utils) +``` + +### 6. Version Control Components + +Each component should be git-trackable: + +```bash +git subtree add --prefix components/mylib https://github.com/user/mylib.git main +``` + +Or use submodules: + +```bash +git submodule add https://github.com/user/mylib.git components/mylib +``` + +### 7. Document Component APIs + +Each component should have clear documentation: + +``` +component/ +├── README.md # Component documentation +├── include/ +│ └── component/ +│ └── api.hpp # Well-documented API +└── src/ +``` + +## Summary + +Understanding these concepts will help you: + +- Structure projects effectively +- Leverage automatic discovery +- Manage dependencies cleanly +- Scale projects maintainably +- Integrate third-party code easily + +## Next Steps + +- [Quick Start](quick-start.md) - Build your first project +- [Examples](examples.md) - See concepts in action +- [API Reference](api.md) - Detailed function documentation diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..f537be2 --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,707 @@ +# Examples and Usage Patterns + +This guide provides real-world examples and common usage patterns for abcmake projects. + +## Table of Contents + +- [Basic Examples](#basic-examples) + - [Simple Executable](#simple-executable) + - [Executable with Libraries](#executable-with-libraries) + - [Header-Only Library](#header-only-library) + - [Shared Library](#shared-library) +- [Component Organization](#component-organization) + - [Nested Components](#nested-components) + - [Multiple Components](#multiple-components) + - [Cross-Component Dependencies](#cross-component-dependencies) +- [Advanced Patterns](#advanced-patterns) + - [Component Registry](#component-registry) + - [Component Sets](#component-sets) + - [Custom Directory Layout](#custom-directory-layout) + - [Third-Party Integration](#third-party-integration) +- [Real-World Projects](#real-world-projects) + - [CLI Application](#cli-application) + - [Plugin Architecture](#plugin-architecture) + - [Embedded System](#embedded-system) + +## Basic Examples + +### Simple Executable + +The simplest possible project - just a main executable. + +**Project Structure:** +``` +simple_app/ +├── CMakeLists.txt +├── ab.cmake +└── src/ + └── main.cpp +``` + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(SimpleApp) + +include(ab.cmake) +add_main_component(${PROJECT_NAME}) +``` + +**src/main.cpp:** +```cpp +#include + +int main() { + std::cout << "Simple app!" << std::endl; + return 0; +} +``` + +### Executable with Libraries + +Application with internal library components. + +**Project Structure:** +``` +app_with_libs/ +├── CMakeLists.txt +├── ab.cmake +├── src/ +│ └── main.cpp +└── components/ + ├── math/ + │ ├── CMakeLists.txt + │ ├── include/math/ + │ │ └── calculator.hpp + │ └── src/ + │ └── calculator.cpp + └── utils/ + ├── CMakeLists.txt + ├── include/utils/ + │ └── logger.hpp + └── src/ + └── logger.cpp +``` + +**Root CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(AppWithLibs) + +include(ab.cmake) +add_main_component(${PROJECT_NAME}) +``` + +**components/math/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(math) + +include(../../ab.cmake) +add_component(${PROJECT_NAME}) +``` + +**components/math/include/math/calculator.hpp:** +```cpp +#pragma once + +class Calculator { +public: + static int add(int a, int b); + static int multiply(int a, int b); +}; +``` + +**components/math/src/calculator.cpp:** +```cpp +#include "math/calculator.hpp" + +int Calculator::add(int a, int b) { + return a + b; +} + +int Calculator::multiply(int a, int b) { + return a * b; +} +``` + +**src/main.cpp:** +```cpp +#include +#include "math/calculator.hpp" +#include "utils/logger.hpp" + +int main() { + Logger::log("Starting application"); + + int result = Calculator::add(5, 3); + Logger::log("5 + 3 = " + std::to_string(result)); + + return 0; +} +``` + +### Header-Only Library + +Creating a header-only component. + +**components/algorithms/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(algorithms) + +include(../../ab.cmake) +add_component(${PROJECT_NAME} INTERFACE) +``` + +**components/algorithms/include/algorithms/sort.hpp:** +```cpp +#pragma once +#include +#include + +namespace algorithms { + +template +void quicksort(std::vector& data) { + std::sort(data.begin(), data.end()); +} + +template +T find_max(const std::vector& data) { + return *std::max_element(data.begin(), data.end()); +} + +} // namespace algorithms +``` + +**Usage in main.cpp:** +```cpp +#include +#include +#include "algorithms/sort.hpp" + +int main() { + std::vector numbers = {5, 2, 8, 1, 9}; + + algorithms::quicksort(numbers); + int max = algorithms::find_max(numbers); + + std::cout << "Max: " << max << std::endl; + return 0; +} +``` + +### Shared Library + +Creating a component as a shared/dynamic library. + +**components/plugin/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(plugin) + +include(../../ab.cmake) +add_component(${PROJECT_NAME} SHARED) + +# Export symbols on Windows +if(WIN32) + target_compile_definitions(${PROJECT_NAME} PRIVATE PLUGIN_EXPORTS) +endif() +``` + +**components/plugin/include/plugin/api.hpp:** +```cpp +#pragma once + +#ifdef _WIN32 + #ifdef PLUGIN_EXPORTS + #define PLUGIN_API __declspec(dllexport) + #else + #define PLUGIN_API __declspec(dllimport) + #endif +#else + #define PLUGIN_API +#endif + +class PLUGIN_API PluginInterface { +public: + virtual ~PluginInterface() = default; + virtual void execute() = 0; +}; + +extern "C" PLUGIN_API PluginInterface* create_plugin(); +extern "C" PLUGIN_API void destroy_plugin(PluginInterface* plugin); +``` + +## Component Organization + +### Nested Components + +Components can have their own sub-components. + +**Project Structure:** +``` +project/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + └── core/ + ├── CMakeLists.txt + ├── src/core.cpp + ├── include/core/core.hpp + └── components/ + ├── subsystem_a/ + │ ├── CMakeLists.txt + │ └── ... + └── subsystem_b/ + ├── CMakeLists.txt + └── ... +``` + +**components/core/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(core) + +include(../../ab.cmake) +add_component(${PROJECT_NAME}) + +# Nested components are automatically discovered and linked +``` + +### Multiple Components + +Managing multiple independent components. + +**Project Structure:** +``` +multi_component_app/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + ├── database/ + │ ├── CMakeLists.txt + │ └── ... + ├── networking/ + │ ├── CMakeLists.txt + │ └── ... + ├── ui/ + │ ├── CMakeLists.txt + │ └── ... + └── config/ + ├── CMakeLists.txt + └── ... +``` + +All components are automatically discovered and linked to the main component. + +### Cross-Component Dependencies + +One component depending on another. + +**components/http_server/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(http_server) + +include(../../ab.cmake) +add_component(${PROJECT_NAME}) + +# Explicitly link to networking component +target_link_components(${PROJECT_NAME} + PATH ${CMAKE_CURRENT_LIST_DIR}/../networking) +``` + +## Advanced Patterns + +### Component Registry + +Using the component registry for name-based linking. + +**Root CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(ab.cmake) + +# Register components by path +register_components( + ${CMAKE_CURRENT_SOURCE_DIR}/components/core + ${CMAKE_CURRENT_SOURCE_DIR}/components/utils + ${CMAKE_CURRENT_SOURCE_DIR}/third_party/vendor_lib +) + +add_main_component(${PROJECT_NAME}) + +# Link by name instead of path +target_link_components(${PROJECT_NAME} + NAME core utils vendor_lib) +``` + +### Component Sets + +Bulk registration without creating local targets. + +**Scenario:** You have many third-party components but only need some. + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(ab.cmake) + +# Register all third-party components without building them here +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party + REGISTER_ALL) + +add_main_component(${PROJECT_NAME}) + +# Now link only what you need +target_link_components(${PROJECT_NAME} + NAME fmt spdlog json) +``` + +**Alternative - Register specific components:** +```cmake +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party + COMPONENTS fmt spdlog json) +``` + +### Custom Directory Layout + +Adapting abcmake to non-standard layouts. + +**Project Structure:** +``` +legacy_project/ +├── CMakeLists.txt +├── source/ # Not "src/" +│ └── main.cpp +├── headers/ # Not "include/" +│ └── app.hpp +└── modules/ # Not "components/" + └── legacy_lib/ + └── ... +``` + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(LegacyApp) + +include(ab.cmake) + +# Specify custom directories +add_main_component(${PROJECT_NAME} + SOURCE_DIR source + INCLUDE_DIR headers) + +# Manually discover components in non-standard location +target_link_components(${PROJECT_NAME} + PATH ${CMAKE_CURRENT_SOURCE_DIR}/modules/legacy_lib) +``` + +### Third-Party Integration + +#### Vendored CMake Package + +**Project Structure:** +``` +project/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + └── fmt/ # Contains fmtConfig.cmake + ├── include/ + ├── src/ + └── fmtConfig.cmake +``` + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(ab.cmake) +add_main_component(${PROJECT_NAME}) + +# fmt is auto-detected and linked via find_package +``` + +**main.cpp:** +```cpp +#include + +int main() { + fmt::print("Hello, {}!\n", "world"); + return 0; +} +``` + +#### FetchContent Integration + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(FetchContent) +include(ab.cmake) + +# Fetch external dependency +FetchContent_Declare( + json + GIT_REPOSITORY https://github.com/nlohmann/json.git + GIT_TAG v3.11.2 +) +FetchContent_MakeAvailable(json) + +add_main_component(${PROJECT_NAME}) + +# Link external dependency +target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) +``` + +## Real-World Projects + +### CLI Application + +A command-line tool with multiple modules. + +**Project Structure:** +``` +cli_tool/ +├── CMakeLists.txt +├── src/main.cpp +└── components/ + ├── cli_parser/ + │ ├── CMakeLists.txt + │ ├── include/cli_parser/ + │ │ └── parser.hpp + │ └── src/parser.cpp + ├── file_processor/ + │ ├── CMakeLists.txt + │ └── ... + └── output_formatter/ + ├── CMakeLists.txt + └── ... +``` + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(CliTool) + +include(ab.cmake) + +# Main executable +add_main_component(${PROJECT_NAME}) + +# All components auto-discovered and linked +``` + +**src/main.cpp:** +```cpp +#include +#include "cli_parser/parser.hpp" +#include "file_processor/processor.hpp" +#include "output_formatter/formatter.hpp" + +int main(int argc, char* argv[]) { + CliParser parser; + auto options = parser.parse(argc, argv); + + FileProcessor processor(options); + auto results = processor.process(); + + OutputFormatter formatter; + formatter.format(results); + + return 0; +} +``` + +### Plugin Architecture + +Application that loads plugins dynamically. + +**Project Structure:** +``` +plugin_app/ +├── CMakeLists.txt +├── src/ +│ ├── main.cpp +│ └── plugin_manager.cpp +├── include/ +│ └── plugin_interface.hpp +└── plugins/ + ├── plugin_a/ + │ ├── CMakeLists.txt + │ └── src/plugin_a.cpp + └── plugin_b/ + ├── CMakeLists.txt + └── src/plugin_b.cpp +``` + +**Root CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(PluginApp) + +include(ab.cmake) + +# Main application +add_main_component(${PROJECT_NAME}) + +# Don't auto-link plugins; they'll be loaded dynamically +# But register them for compilation +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/plugins + REGISTER_ALL) +``` + +**plugins/plugin_a/CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(plugin_a) + +include(../../ab.cmake) + +# Create shared library +add_component(${PROJECT_NAME} SHARED) + +# Link to main app's interface +target_include_directories(${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) +``` + +### Embedded System + +Firmware project for embedded systems. + +**Project Structure:** +``` +firmware/ +├── CMakeLists.txt +├── src/main.c +└── components/ + ├── hal/ # Hardware abstraction + │ ├── CMakeLists.txt + │ └── ... + ├── drivers/ # Device drivers + │ ├── CMakeLists.txt + │ └── ... + └── rtos/ # RTOS components + ├── CMakeLists.txt + └── ... +``` + +**CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(Firmware C) + +# Set target architecture +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_C_COMPILER arm-none-eabi-gcc) + +include(ab.cmake) + +add_main_component(${PROJECT_NAME}) + +# Add embedded-specific flags +target_compile_options(${PROJECT_NAME} PRIVATE + -mcpu=cortex-m4 + -mthumb + -Wall -Werror +) + +target_link_options(${PROJECT_NAME} PRIVATE + -T${CMAKE_CURRENT_SOURCE_DIR}/linker_script.ld +) +``` + +## Tips and Best Practices + +### Organizing Large Projects + +``` +large_project/ +├── CMakeLists.txt +├── apps/ # Multiple executables +│ ├── server/ +│ │ ├── CMakeLists.txt +│ │ └── src/main.cpp +│ └── client/ +│ ├── CMakeLists.txt +│ └── src/main.cpp +├── libs/ # Shared components +│ └── components/ +│ ├── core/ +│ ├── network/ +│ └── utils/ +└── third_party/ # External dependencies + └── components/ + ├── fmt/ + └── spdlog/ +``` + +**Root CMakeLists.txt:** +```cmake +cmake_minimum_required(VERSION 3.15) +project(LargeProject) + +include(ab.cmake) + +# Register all shared libraries +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/libs/components + REGISTER_ALL) + +add_component_set( + PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party/components + REGISTER_ALL) + +# Build applications +add_subdirectory(apps/server) +add_subdirectory(apps/client) +``` + +### Testing Integration + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyProject) + +include(ab.cmake) + +# Production code +add_main_component(${PROJECT_NAME}) + +# Testing +if(BUILD_TESTING) + enable_testing() + + # Test executable with access to components + add_executable(unit_tests tests/test_main.cpp) + + # Link same components as main app + target_link_components(unit_tests + PATH ${CMAKE_CURRENT_SOURCE_DIR}/components/math + ${CMAKE_CURRENT_SOURCE_DIR}/components/utils) + + add_test(NAME unit_tests COMMAND unit_tests) +endif() +``` + +## Next Steps + +- [API Reference](api.md) - Detailed function documentation +- [Concepts](concepts.md) - Understand the underlying model +- [Contributing](../CONTRIBUTING.md) - Contribute your own examples diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..c308c60 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,370 @@ +# Installation Guide + +This guide covers different methods to install and use abcmake in your projects. + +## Table of Contents + +- [Quick Installation](#quick-installation) +- [Project-Scoped Installation](#project-scoped-installation) +- [User-Scoped Installation](#user-scoped-installation) +- [System-Wide Installation](#system-wide-installation) +- [Verification](#verification) +- [Troubleshooting](#troubleshooting) + +## Quick Installation + +For most users, the **project-scoped** approach is the simplest and most portable option. + +## Project-Scoped Installation + +This method bundles abcmake directly with your project - ideal for portability and version control. + +### Step 1: Download the Single-File Distribution + +Download `ab.cmake` from the [latest release](https://github.com/an-dr/abcmake/releases) and place it in your project root (or a subdirectory like `cmake/`). + +### Step 2: Include in Your CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyProject) + +# Include abcmake +include(ab.cmake) +# or: include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ab.cmake) + +# Use abcmake +add_main_component(${PROJECT_NAME}) +``` + +### Advantages + +- **Portable**: Works anywhere without system installation +- **Version control**: Track abcmake version with your project +- **No dependencies**: Self-contained single file +- **Team-friendly**: Everyone uses the same version + +### Disadvantages + +- Need to update `ab.cmake` manually for new versions +- File duplicated across multiple projects + +## User-Scoped Installation + +Install abcmake for your user account - useful when working on multiple projects. + +### Linux and macOS + +```bash +# Clone the repository +git clone https://github.com/an-dr/abcmake.git +cd abcmake + +# Build and install to user directory +cmake -B build +cmake --install build --prefix ~/.local +``` + +Add to your CMakeLists.txt: + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyProject) + +# CMake automatically searches ~/.local +find_package(abcmake REQUIRED) + +add_main_component(${PROJECT_NAME}) +``` + +### Windows + +```powershell +# Clone the repository +git clone https://github.com/an-dr/abcmake.git +cd abcmake + +# Build and install to user AppData +cmake -B build +cmake --install build --prefix "$env:LOCALAPPDATA\CMake" +``` + +Add to your CMakeLists.txt: + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyProject) + +# Help CMake find the package +list(APPEND CMAKE_PREFIX_PATH "$ENV{LOCALAPPDATA}/CMake") +find_package(abcmake REQUIRED) + +add_main_component(${PROJECT_NAME}) +``` + +### Advantages + +- Available across all your projects +- Easy to update (re-run install) +- No admin/root privileges needed + +### Disadvantages + +- Not included in version control +- Team members need separate installation +- Path configuration may be needed + +## System-Wide Installation + +Install abcmake for all users on the system - best for shared development machines. + +### Linux + +```bash +git clone https://github.com/an-dr/abcmake.git +cd abcmake + +cmake -B build +sudo cmake --install build --prefix /usr/local +``` + +### macOS + +```bash +git clone https://github.com/an-dr/abcmake.git +cd abcmake + +cmake -B build +sudo cmake --install build --prefix /usr/local +``` + +### Windows (Administrator) + +```powershell +# Run PowerShell as Administrator +git clone https://github.com/an-dr/abcmake.git +cd abcmake + +cmake -B build +cmake --install build --prefix "C:\Program Files\CMake" +``` + +Use in your project: + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyProject) + +find_package(abcmake REQUIRED) +add_main_component(${PROJECT_NAME}) +``` + +### Advantages + +- Available to all users +- No per-project or per-user setup needed +- Clean CMakeLists.txt + +### Disadvantages + +- Requires administrator privileges +- May conflict with other versions +- Not portable across machines + +## Verification + +After installation, verify abcmake is working: + +### For find_package() Installations + +Create a minimal test project: + +```cmake +# test/CMakeLists.txt +cmake_minimum_required(VERSION 3.15) +project(AbcmakeTest) + +find_package(abcmake REQUIRED) + +message(STATUS "abcmake found successfully!") +message(STATUS "abcmake version: ${abcmake_VERSION}") +``` + +Run: + +```bash +cd test +cmake -B build +``` + +Expected output: +``` +-- abcmake found successfully! +-- abcmake version: X.Y.Z +``` + +### For include() Installations + +Create a minimal test: + +```cmake +# test/CMakeLists.txt +cmake_minimum_required(VERSION 3.15) +project(AbcmakeTest) + +include(ab.cmake) + +message(STATUS "abcmake loaded successfully!") + +# Create a dummy component +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test.cpp" "int main() { return 0; }") +add_main_component(test SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}) +``` + +## Troubleshooting + +### CMake Cannot Find abcmake Package + +**Problem**: `find_package(abcmake REQUIRED)` fails with "Could not find abcmake" + +**Solutions**: + +1. **Check installation path**: + ```bash + # Linux/macOS + ls ~/.local/lib/cmake/abcmake/ + ls /usr/local/lib/cmake/abcmake/ + + # Windows + dir "%LOCALAPPDATA%\CMake\lib\cmake\abcmake\" + dir "C:\Program Files\CMake\lib\cmake\abcmake\" + ``` + +2. **Add to CMAKE_PREFIX_PATH**: + ```cmake + list(APPEND CMAKE_PREFIX_PATH "/path/to/install/prefix") + find_package(abcmake REQUIRED) + ``` + +3. **Use environment variable**: + ```bash + export CMAKE_PREFIX_PATH="/path/to/install/prefix:$CMAKE_PREFIX_PATH" + cmake -B build + ``` + +### Permission Denied During Installation + +**Problem**: Installation fails with permission errors + +**Solutions**: + +1. Use user-scoped installation instead of system-wide +2. Use `sudo` on Linux/macOS +3. Run PowerShell as Administrator on Windows +4. Check directory permissions + +### Wrong abcmake Version Loaded + +**Problem**: CMake loads a different version than expected + +**Solutions**: + +1. **Remove old installations**: + ```bash + # Find all installations + cmake --find-package -DNAME=abcmake -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=EXIST + ``` + +2. **Specify version requirement**: + ```cmake + find_package(abcmake 1.2.3 EXACT REQUIRED) + ``` + +3. **Clear CMake cache**: + ```bash + rm -rf build/ + cmake -B build + ``` + +### include(ab.cmake) File Not Found + +**Problem**: `include(ab.cmake)` fails to find the file + +**Solutions**: + +1. **Use absolute path**: + ```cmake + include(${CMAKE_CURRENT_SOURCE_DIR}/ab.cmake) + ``` + +2. **Use CMAKE_MODULE_PATH**: + ```cmake + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + include(ab.cmake) # looks in cmake/ directory + ``` + +3. **Verify file exists**: + ```bash + ls ab.cmake + ls cmake/ab.cmake + ``` + +## Updating abcmake + +### Project-Scoped Updates + +Download the new `ab.cmake` from releases and replace the old file: + +```bash +# Backup current version +cp ab.cmake ab.cmake.backup + +# Download new version (example using curl) +curl -L -o ab.cmake https://github.com/an-dr/abcmake/releases/download/vX.Y.Z/ab.cmake + +# Test with your project +cmake -B build + +# If successful, commit +git add ab.cmake +git commit -m "Update abcmake to vX.Y.Z" +``` + +### User/System-Scoped Updates + +Re-run the installation: + +```bash +cd abcmake +git pull origin main +cmake -B build +cmake --install build --prefix ~/.local # or your preferred prefix +``` + +## Uninstallation + +### Project-Scoped + +Simply delete the `ab.cmake` file and remove the `include()` line from CMakeLists.txt. + +### User/System-Scoped + +```bash +# From the build directory +cmake --build build --target uninstall + +# Or manually remove installation +rm -rf ~/.local/lib/cmake/abcmake +rm -rf /usr/local/lib/cmake/abcmake +``` + +Windows: +```powershell +Remove-Item -Recurse "$env:LOCALAPPDATA\CMake\lib\cmake\abcmake" +``` + +## Next Steps + +- [Quick Start Guide](quick-start.md) - Get started with your first project +- [API Reference](api.md) - Detailed function documentation +- [Examples](examples.md) - Real-world usage examples diff --git a/docs/quick-start.md b/docs/quick-start.md new file mode 100644 index 0000000..bb8ae21 --- /dev/null +++ b/docs/quick-start.md @@ -0,0 +1,353 @@ +# Quick Start Guide + +Get up and running with abcmake in minutes. This guide walks you through creating your first abcmake project from scratch. + +## Prerequisites + +- CMake 3.15 or higher +- C or C++ compiler (GCC, Clang, MSVC, etc.) +- Basic familiarity with CMake + +## 5-Minute Setup + +### Step 1: Create Project Structure + +```bash +mkdir my_project +cd my_project +mkdir src components +``` + +### Step 2: Download abcmake + +Download `ab.cmake` from [releases](https://github.com/an-dr/abcmake/releases) and place it in your project root. + +Or use curl/wget: + +```bash +# Using curl +curl -L -o ab.cmake https://github.com/an-dr/abcmake/releases/latest/download/ab.cmake + +# Using wget +wget -O ab.cmake https://github.com/an-dr/abcmake/releases/latest/download/ab.cmake +``` + +### Step 3: Create Main CMakeLists.txt + +Create `CMakeLists.txt` in the project root: + +```cmake +cmake_minimum_required(VERSION 3.15) +project(MyApp) + +include(ab.cmake) + +add_main_component(${PROJECT_NAME}) +``` + +### Step 4: Write Your Main Source + +Create `src/main.cpp`: + +```cpp +#include + +int main() { + std::cout << "Hello from abcmake!" << std::endl; + return 0; +} +``` + +### Step 5: Build and Run + +```bash +cmake -B build +cmake --build build +./build/MyApp # or .\build\Debug\MyApp.exe on Windows +``` + +Expected output: +``` +Hello from abcmake! +``` + +Congratulations! You've created your first abcmake project. + +## Adding Your First Component + +Let's add a reusable library component. + +### Step 1: Create Component Structure + +```bash +mkdir -p components/greeter/src +mkdir -p components/greeter/include/greeter +``` + +### Step 2: Create Component CMakeLists.txt + +Create `components/greeter/CMakeLists.txt`: + +```cmake +cmake_minimum_required(VERSION 3.15) +project(greeter) + +find_package(abcmake REQUIRED) +# or: include(../../ab.cmake) + +add_component(${PROJECT_NAME}) +``` + +### Step 3: Create Component Header + +Create `components/greeter/include/greeter/greeter.hpp`: + +```cpp +#pragma once +#include + +class Greeter { +public: + explicit Greeter(const std::string& name); + std::string greet() const; + +private: + std::string name_; +}; +``` + +### Step 4: Create Component Implementation + +Create `components/greeter/src/greeter.cpp`: + +```cpp +#include "greeter/greeter.hpp" + +Greeter::Greeter(const std::string& name) : name_(name) {} + +std::string Greeter::greet() const { + return "Hello, " + name_ + "!"; +} +``` + +### Step 5: Use Component in Main + +Update `src/main.cpp`: + +```cpp +#include +#include "greeter/greeter.hpp" + +int main() { + Greeter greeter("World"); + std::cout << greeter.greet() << std::endl; + return 0; +} +``` + +### Step 6: Rebuild + +```bash +cmake -B build +cmake --build build +./build/MyApp +``` + +Expected output: +``` +Hello, World! +``` + +That's it! abcmake automatically: +- Discovered the `greeter` component +- Linked it to your main executable +- Set up include paths +- Handled all dependencies + +## Project Structure Overview + +Your project should now look like this: + +``` +my_project/ +├── CMakeLists.txt # Main build configuration +├── ab.cmake # abcmake single-file distribution +├── src/ +│ └── main.cpp # Main application source +├── include/ # (optional) Public headers for main +└── components/ + └── greeter/ # Your first component + ├── CMakeLists.txt # Component build configuration + ├── include/ + │ └── greeter/ + │ └── greeter.hpp # Public component header + └── src/ + └── greeter.cpp # Component implementation +``` + +## Understanding What Happened + +Let's break down the magic: + +### 1. Main Component Discovery + +```cmake +add_main_component(${PROJECT_NAME}) +``` + +This single line: +- Created an executable target named `MyApp` +- Automatically globbed sources from `src/` +- Discovered all components under `components/` +- Linked discovered components automatically + +### 2. Component Definition + +```cmake +add_component(${PROJECT_NAME}) +``` + +This: +- Created a library target named `greeter` +- Globbed sources from `src/` +- Exposed headers from `include/` +- Made the component linkable by name + +### 3. Automatic Linking + +abcmake automatically: +- Found the `greeter` component in `components/greeter/` +- Added `components/greeter/include/` to include paths +- Linked the greeter library to MyApp +- Created a `greeter::greeter` alias for external projects + +## Common Patterns + +### Multiple Source Directories + +```cmake +add_component(mylib + SOURCE_DIR src core utils + INCLUDE_DIR include public_api) +``` + +### Shared Library + +```cmake +add_component(mylib SHARED) +``` + +### Header-Only Library + +```cmake +add_component(mylib INTERFACE) +``` + +### Manual Component Linking + +```cmake +# Link specific components by path +target_link_components(MyApp + PATH ${CMAKE_CURRENT_LIST_DIR}/components/greeter) +``` + +## Next Steps + +Now that you've created a basic project, explore more advanced features: + +### Learn Core Concepts +- [Concepts Guide](concepts.md) - Understand components, registry, and auto-packages + +### Explore Examples +- [Examples](examples.md) - Real-world usage patterns and recipes + +### Deep Dive into API +- [API Reference](api.md) - Complete function documentation + +### Advanced Topics +- Nested components +- Component sets +- Vendored CMake packages +- Component registry +- Custom project layouts + +## Common First-Time Issues + +### Issue: "No sources found" + +**Problem**: abcmake can't find your source files + +**Solution**: Ensure sources are in `src/` or specify `SOURCE_DIR`: + +```cmake +add_main_component(MyApp SOURCE_DIR source) +``` + +### Issue: Header not found + +**Problem**: `#include "myheader.hpp"` fails + +**Solutions**: + +1. Check header is in `include/` directory +2. Use component-namespaced path: `#include "greeter/greeter.hpp"` +3. Specify custom include dir: + ```cmake + add_component(mylib INCLUDE_DIR headers) + ``` + +### Issue: Multiple definition errors + +**Problem**: Linker complains about multiple definitions + +**Solution**: Ensure header-only code uses `INTERFACE`: + +```cmake +add_component(header_only INTERFACE) +``` + +### Issue: Component not auto-discovered + +**Problem**: Component in `components/` not linked automatically + +**Solutions**: + +1. Ensure component has its own `CMakeLists.txt` with `add_component()` +2. Check component is in a subdirectory of `components/` +3. Verify component CMakeLists.txt is valid + +## Tips for Success + +1. **Keep components focused** - One logical module per component +2. **Use include namespacing** - `include/componentname/header.hpp` +3. **Start simple** - Add complexity as needed +4. **Check compile_commands.json** - Generated automatically for IDE support +5. **Use consistent naming** - Match component directory name with target name + +## Quick Reference Card + +```cmake +# Main executable/library +add_main_component(name [SOURCE_DIR ...] [INCLUDE_DIR ...]) + +# Library component +add_component(name [SHARED|INTERFACE] [SOURCE_DIR ...] [INCLUDE_DIR ...]) + +# Manual linking +target_link_components(target PATH path1 path2 NAME comp1 comp2) + +# Component registration +register_components(path1 path2 ...) + +# Bulk component registration +add_component_set([PATH path] [COMPONENTS ...] [REGISTER_ALL]) +``` + +## Getting Help + +- Check [Examples](examples.md) for similar use cases +- Review [API Reference](api.md) for function details +- Search [GitHub Issues](https://github.com/an-dr/abcmake/issues) +- Read [Concepts Guide](concepts.md) for deeper understanding + +Happy building with abcmake! diff --git a/scripts/build_package.ps1 b/scripts/build_package.ps1 new file mode 100644 index 0000000..fd2a9ef --- /dev/null +++ b/scripts/build_package.ps1 @@ -0,0 +1,22 @@ +$ErrorActionPreference = "Stop" +Set-Location "$PSScriptRoot/.." + +$RepoRoot = (Resolve-Path ".").Path +$BuildDir = Join-Path $RepoRoot "build/package" +$Prefix = Join-Path $RepoRoot "dist/package" +$Config = "Release" +$Generator = "Ninja" + +Write-Host "? Configure" +cmake -S $RepoRoot -B $BuildDir -DCMAKE_INSTALL_PREFIX="$Prefix" -DCMAKE_BUILD_TYPE=$Config -G $Generator +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "? Build" +cmake --build "$BuildDir" --config $Config +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "? Install" +cmake --install "$BuildDir" --config $Config +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "? Installed to $Prefix" diff --git a/src/ab.cmake b/src/ab.cmake index 9780083..1295a53 100644 --- a/src/ab.cmake +++ b/src/ab.cmake @@ -1,6 +1,6 @@ # ************************************************************************* # -# Copyright (c) 2024 Andrei Gramakov. All rights reserved. +# Copyright (c) 2025 Andrei Gramakov. All rights reserved. # # This file is licensed under the terms of the MIT license. # For a copy, see: https://opensource.org/licenses/MIT diff --git a/src/abcmake/add_component.cmake b/src/abcmake/add_component.cmake index 897e97d..31f10d6 100644 --- a/src/abcmake/add_component.cmake +++ b/src/abcmake/add_component.cmake @@ -29,10 +29,32 @@ endfunction() # Add all source files from the specified directory to the target # @param TARGETNAME - name of the target to add sources +# @param SOURCE_DIR - path to the source directory +# @param INTERFACE - if set, use INTERFACE visibility instead of PRIVATE function(target_sources_directory TARGETNAME SOURCE_DIR) + cmake_parse_arguments(arg "INTERFACE" "" "" ${ARGN}) file(GLOB_RECURSE SOURCES "${SOURCE_DIR}/*.cpp" "${SOURCE_DIR}/*.c") - message( DEBUG "[target_sources_directory] ${TARGETNAME} sources: ${SOURCES}") - target_sources(${TARGETNAME} PRIVATE ${SOURCES}) + message(DEBUG "[target_sources_directory] ${TARGETNAME} sources: ${SOURCES}") + if (arg_INTERFACE) + target_sources(${TARGETNAME} INTERFACE ${SOURCES}) + else() + target_sources(${TARGETNAME} PRIVATE ${SOURCES}) + endif() +endfunction() + +# Create a simple namespaced alias (::) for a library target if not already present. +function(_abcmake_add_namespace_alias TARGETNAME) + if (NOT TARGET ${TARGETNAME}) + return() + endif() + get_target_property(_abc_type ${TARGETNAME} TYPE) + if (_abc_type STREQUAL "EXECUTABLE") + return() # aliases for executables are not supported + endif() + set(_abc_alias "${TARGETNAME}::${TARGETNAME}") + if (NOT TARGET ${_abc_alias}) + add_library(${_abc_alias} ALIAS ${TARGETNAME}) + endif() endfunction() # Install the target near the build directory @@ -69,8 +91,9 @@ endfunction() # @param TARGETNAME - name of the target to initialize # @param INCLUDE_DIR - path to the include directory # @param SOURCE_DIR - path to the source directory +# @param INTERFACE - if set, treat as interface library (sources compile in consumer) function(_abcmake_target_init TARGETNAME) - set(flags) + set(flags INTERFACE) set(args) set(listArgs INCLUDE_DIR SOURCE_DIR) cmake_parse_arguments(arg "${flags}" "${args}" "${listArgs}" ${ARGN}) @@ -98,16 +121,65 @@ function(_abcmake_target_init TARGETNAME) # Add target to the target list _abcmake_append_prop_curdir(${ABCMAKE_DIRPROP_TARGETS} ${TARGETNAME}) - + foreach(s ${arg_SOURCE_DIR}) - target_sources_directory(${TARGETNAME} ${s}) + if (arg_INTERFACE) + target_sources_directory(${TARGETNAME} ${s} INTERFACE) + else() + target_sources_directory(${TARGETNAME} ${s}) + endif() endforeach() - - target_include_directories(${TARGETNAME} PUBLIC ${arg_INCLUDE_DIR}) + + if (arg_INTERFACE) + target_include_directories(${TARGETNAME} INTERFACE ${arg_INCLUDE_DIR}) + else() + target_include_directories(${TARGETNAME} PUBLIC ${arg_INCLUDE_DIR}) + endif() _abcmake_add_components(${process_level} ${TARGETNAME}) endfunction() +# Add a component set that only registers nested components (no targets) +# @param PATH - base directory containing components (defaults to abcmake components dir) +# @param COMPONENTS - names of subdirectories to register +# @param REGISTER_ALL - if set, register every subdirectory under PATH +function(add_component_set) + set(flags REGISTER_ALL) + set(args) + set(listArgs PATH COMPONENTS) + cmake_parse_arguments(arg "${flags}" "${args}" "${listArgs}" ${ARGN}) + + if (NOT arg_PATH) + _abcmake_get_components(arg_PATH) + endif() + + set(base_dir ${arg_PATH}) + if (NOT IS_ABSOLUTE "${base_dir}") + set(base_dir "${CMAKE_CURRENT_SOURCE_DIR}/${base_dir}") + endif() + + set(to_register ${arg_COMPONENTS}) + + if (arg_REGISTER_ALL) + file(GLOB children RELATIVE "${base_dir}" "${base_dir}/*") + foreach(child ${children}) + if (IS_DIRECTORY "${base_dir}/${child}") + list(APPEND to_register ${child}) + endif() + endforeach() + endif() + + list(REMOVE_DUPLICATES to_register) + foreach(child ${to_register}) + register_components("${base_dir}/${child}") + endforeach() + + _abcmake_set_prop_curdir(${ABCMAKE_DIRPROP_VERSION} ${ABCMAKE_VERSION}) + _abcmake_set_prop_curdir(${ABCMAKE_DIRPROP_COMPONENT_NAME} ${PROJECT_NAME}) + _abcmake_set_prop_curdir(${ABCMAKE_DIRPROP_TARGETS} "") + _abcmake_log_header(0 "${PROJECT_NAME} (component set)") +endfunction() + # Add an executable component to the project # @param TARGETNAME - name of the target to add the component # @param INCLUDE_DIR - path to the include directory @@ -151,9 +223,10 @@ endfunction() # @param TARGETNAME - name of the target to add the component # @param INCLUDE_DIR - paths to the include directories # @param SOURCE_DIR - paths to the source directories -# @param SHARED - if set to TRUE, the library will be shared +# @param SHARED - if set, the library will be shared +# @param INTERFACE - if set, the library will be an interface library function(add_component TARGETNAME) - set(flags SHARED) + set(flags SHARED INTERFACE) set(args) set(listArgs INCLUDE_DIR SOURCE_DIR) cmake_parse_arguments(arg "${flags}" "${args}" "${listArgs}" ${ARGN}) @@ -171,25 +244,37 @@ function(add_component TARGETNAME) endif() endif() - if (arg_SHARED) + if (arg_INTERFACE) + add_library(${TARGETNAME} INTERFACE) + elseif (arg_SHARED) add_library(${TARGETNAME} SHARED) else() add_library(${TARGETNAME} STATIC) endif() - + + _abcmake_add_namespace_alias(${TARGETNAME}) + message(DEBUG "[add_component] TARGETNAME: ${TARGETNAME}") message(DEBUG "[add_component] INCLUDE_DIR: ${arg_INCLUDE_DIR}") message(DEBUG "[add_component] SOURCE_DIR: ${arg_SOURCE_DIR}") message(DEBUG "[add_component] SHARED: ${arg_SHARED}") - + message(DEBUG "[add_component] INTERFACE: ${arg_INTERFACE}") + # Set Component Src and Include _abcmake_set_prop_curdir("${ABCMAKE_DIRPROP_SRC}" "${arg_SOURCE_DIR}") _abcmake_set_prop_curdir("${ABCMAKE_DIRPROP_INCLUDE}" "${arg_INCLUDE_DIR}") - - _abcmake_target_init(${TARGETNAME} - INCLUDE_DIR ${arg_INCLUDE_DIR} - SOURCE_DIR ${arg_SOURCE_DIR}) - _abcmake_target_install(${TARGETNAME} ${ABC_INSTALL_LIB_SUBDIR}) + + if (arg_INTERFACE) + _abcmake_target_init(${TARGETNAME} + INTERFACE + INCLUDE_DIR ${arg_INCLUDE_DIR} + SOURCE_DIR ${arg_SOURCE_DIR}) + else() + _abcmake_target_init(${TARGETNAME} + INCLUDE_DIR ${arg_INCLUDE_DIR} + SOURCE_DIR ${arg_SOURCE_DIR}) + _abcmake_target_install(${TARGETNAME} ${ABC_INSTALL_LIB_SUBDIR}) + endif() endfunction() # add_component.cmake ========================================================== diff --git a/src/abcmake/target_link_components.cmake b/src/abcmake/target_link_components.cmake index 982d8df..11f2011 100644 --- a/src/abcmake/target_link_components.cmake +++ b/src/abcmake/target_link_components.cmake @@ -1,6 +1,16 @@ # ============================================================================== # target_link_components.cmake ================================================= +# Get appropriate visibility for target_link_libraries based on target type +function(_abcmake_get_link_visibility TARGETNAME OUT_VISIBILITY) + get_target_property(_type ${TARGETNAME} TYPE) + if (_type STREQUAL "INTERFACE_LIBRARY") + set(${OUT_VISIBILITY} INTERFACE PARENT_SCOPE) + else() + set(${OUT_VISIBILITY} PRIVATE PARENT_SCOPE) + endif() +endfunction() + # Link internal abcmake component if present. # Returns TRUE in OUT_LINKED if a link action happened. function(_abcmake_try_link_abcmake_component PROCESS_LEVEL TARGETNAME COMPONENTPATH OUT_LINKED) @@ -9,7 +19,8 @@ function(_abcmake_try_link_abcmake_component PROCESS_LEVEL TARGETNAME COMPONENTP if (ver) _abcmake_get_prop_dir(${COMPONENTPATH} ${ABCMAKE_DIRPROP_TARGETS} to_link) if (to_link) - target_link_libraries(${TARGETNAME} PRIVATE ${to_link}) + _abcmake_get_link_visibility(${TARGETNAME} _vis) + target_link_libraries(${TARGETNAME} ${_vis} ${to_link}) _abcmake_log_ok(${PROCESS_LEVEL} "${TARGETNAME}: linked ${to_link}") set(${OUT_LINKED} TRUE PARENT_SCOPE) endif() @@ -23,6 +34,7 @@ function(_abcmake_try_link_cmake_package PROCESS_LEVEL TARGETNAME COMPONENTPATH set(linked FALSE) if (EXISTS ${COMPONENTPATH}) file(GLOB _abc_pkg_configs "${COMPONENTPATH}/*Config.cmake") + _abcmake_get_link_visibility(${TARGETNAME} _vis) foreach(_abc_pkg_cfg ${_abc_pkg_configs}) get_filename_component(_abc_pkg_cfg_name "${_abc_pkg_cfg}" NAME_WE) string(REGEX REPLACE "Config$" "" _abc_pkg_name "${_abc_pkg_cfg_name}") @@ -31,11 +43,11 @@ function(_abcmake_try_link_cmake_package PROCESS_LEVEL TARGETNAME COMPONENTPATH endif() find_package(${_abc_pkg_name} CONFIG PATHS "${COMPONENTPATH}" NO_DEFAULT_PATH QUIET) if (TARGET ${_abc_pkg_name}::${_abc_pkg_name}) - target_link_libraries(${TARGETNAME} PRIVATE ${_abc_pkg_name}::${_abc_pkg_name}) + target_link_libraries(${TARGETNAME} ${_vis} ${_abc_pkg_name}::${_abc_pkg_name}) _abcmake_log_ok(${PROCESS_LEVEL} "${TARGETNAME}: linked package ${_abc_pkg_name}::${_abc_pkg_name}") set(linked TRUE) elseif (TARGET ${_abc_pkg_name}) - target_link_libraries(${TARGETNAME} PRIVATE ${_abc_pkg_name}) + target_link_libraries(${TARGETNAME} ${_vis} ${_abc_pkg_name}) _abcmake_log_ok(${PROCESS_LEVEL} "${TARGETNAME}: linked package target ${_abc_pkg_name}") set(linked TRUE) else() diff --git a/src/abcmakeConfig.cmake b/src/abcmakeConfig.cmake new file mode 100644 index 0000000..efb1ba7 --- /dev/null +++ b/src/abcmakeConfig.cmake @@ -0,0 +1,49 @@ + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was abcmakeConfig.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +# abcmake Package Configuration File +# +# This file sets up the abcmake build system for use in other projects. +# After installation, use it with: +# find_package(abcmake REQUIRED) + +# Compute the installation prefix relative to this file +get_filename_component(ABCMAKE_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# Set ABCMAKE_PATH to the installation directory +set(ABCMAKE_PATH "${ABCMAKE_CMAKE_DIR}") + +# Make abcmake functions available +include("${ABCMAKE_CMAKE_DIR}/ab.cmake") + +check_required_components(abcmake) + +# Provide helpful variables for users +set(abcmake_FOUND TRUE) +set(abcmake_VERSION "${ABCMAKE_VERSION}") +set(abcmake_DIR "${ABCMAKE_CMAKE_DIR}") + +message(STATUS "Found abcmake: ${abcmake_VERSION} (${ABCMAKE_CMAKE_DIR})") diff --git a/src/abcmakeConfigVersion.cmake b/src/abcmakeConfigVersion.cmake new file mode 100644 index 0000000..1b7b8bd --- /dev/null +++ b/src/abcmakeConfigVersion.cmake @@ -0,0 +1,41 @@ +# ************************************************************************* +# +# Copyright (c) 2025 Andrei Gramakov. All rights reserved. +# +# This file is licensed under the terms of the MIT license. +# For a copy, see: https://opensource.org/licenses/MIT +# +# site: https://agramakov.me +# e-mail: mail@agramakov.me +# +# Andrei's Build CMake subsystem or abcmake is a CMake module to work +# with C/C++ project of a predefined standard structure in order to +# simplify the build process. +# +# Source Code: https://github.com/an-dr/abcmake +# ************************************************************************* + +include("${CMAKE_CURRENT_LIST_DIR}/version.cmake") + +set(PACKAGE_VERSION "${ABCMAKE_VERSION}") + +# basic compatibility: same major, >= requested +if(PACKAGE_FIND_VERSION) + if(ABCMAKE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + else() + string(REGEX MATCH "^([0-9]+)" _major "${ABCMAKE_VERSION}") + set(_this_major "${CMAKE_MATCH_1}") + string(REGEX MATCH "^([0-9]+)" _req_major "${PACKAGE_FIND_VERSION}") + if(_this_major STREQUAL _req_major) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(ABCMAKE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + endif() +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) +endif() diff --git a/src/version.cmake b/src/version.cmake index 58fe869..48c7802 100644 --- a/src/version.cmake +++ b/src/version.cmake @@ -1 +1 @@ -set(ABCMAKE_VERSION 6.2.0) +set(ABCMAKE_VERSION 6.3.0) diff --git a/tests/test_alias_link/CMakeLists.txt b/tests/test_alias_link/CMakeLists.txt new file mode 100644 index 0000000..0be5539 --- /dev/null +++ b/tests/test_alias_link/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(AliasConsumer) + +add_subdirectory(component_proj) + +add_executable(app src/main.cpp) +target_link_libraries(app PRIVATE nested_alias::nested_alias) diff --git a/tests/test_alias_link/component_proj/CMakeLists.txt b/tests/test_alias_link/component_proj/CMakeLists.txt new file mode 100644 index 0000000..3bd15f7 --- /dev/null +++ b/tests/test_alias_link/component_proj/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.15) +project(NestedAliasProject) + +include($ENV{ABCMAKE_PATH}/ab.cmake) + +add_component(nested_alias + INCLUDE_DIR include + SOURCE_DIR src) diff --git a/tests/test_alias_link/component_proj/include/nested_alias.hpp b/tests/test_alias_link/component_proj/include/nested_alias.hpp new file mode 100644 index 0000000..6e449f6 --- /dev/null +++ b/tests/test_alias_link/component_proj/include/nested_alias.hpp @@ -0,0 +1,3 @@ +#pragma once + +int nested_alias(); diff --git a/tests/test_alias_link/component_proj/src/nested_alias.cpp b/tests/test_alias_link/component_proj/src/nested_alias.cpp new file mode 100644 index 0000000..314534a --- /dev/null +++ b/tests/test_alias_link/component_proj/src/nested_alias.cpp @@ -0,0 +1,5 @@ +#include "nested_alias.hpp" + +int nested_alias() { + return 0; +} diff --git a/tests/test_alias_link/src/main.cpp b/tests/test_alias_link/src/main.cpp new file mode 100644 index 0000000..ad09b5e --- /dev/null +++ b/tests/test_alias_link/src/main.cpp @@ -0,0 +1,5 @@ +#include "nested_alias.hpp" + +int main() { + return nested_alias(); +} diff --git a/tests/test_component_set/CMakeLists.txt b/tests/test_component_set/CMakeLists.txt new file mode 100644 index 0000000..d3727bc --- /dev/null +++ b/tests/test_component_set/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(ComponentSetMain) + +include($ENV{ABCMAKE_PATH}/ab.cmake) + +add_main_component(${PROJECT_NAME}) +target_link_components(${PROJECT_NAME} + PATH ${CMAKE_CURRENT_LIST_DIR}/components/aggregator + NAME nested_hello) diff --git a/tests/test_component_set/components/aggregator/CMakeLists.txt b/tests/test_component_set/components/aggregator/CMakeLists.txt new file mode 100644 index 0000000..e8a968d --- /dev/null +++ b/tests/test_component_set/components/aggregator/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) +project(ComponentSetAggregator) + +include($ENV{ABCMAKE_PATH}/ab.cmake) + +add_component_set(REGISTER_ALL) diff --git a/tests/test_component_set/components/aggregator/components/nested_hello/CMakeLists.txt b/tests/test_component_set/components/aggregator/components/nested_hello/CMakeLists.txt new file mode 100644 index 0000000..e3e4d49 --- /dev/null +++ b/tests/test_component_set/components/aggregator/components/nested_hello/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) +project(nested_hello) + +include($ENV{ABCMAKE_PATH}/ab.cmake) + +add_component(${PROJECT_NAME}) diff --git a/tests/test_component_set/components/aggregator/components/nested_hello/include/nested_hello.hpp b/tests/test_component_set/components/aggregator/components/nested_hello/include/nested_hello.hpp new file mode 100644 index 0000000..e255c67 --- /dev/null +++ b/tests/test_component_set/components/aggregator/components/nested_hello/include/nested_hello.hpp @@ -0,0 +1,3 @@ +#pragma once + +int nested_hello(); diff --git a/tests/test_component_set/components/aggregator/components/nested_hello/src/nested_hello.cpp b/tests/test_component_set/components/aggregator/components/nested_hello/src/nested_hello.cpp new file mode 100644 index 0000000..f4987bf --- /dev/null +++ b/tests/test_component_set/components/aggregator/components/nested_hello/src/nested_hello.cpp @@ -0,0 +1,5 @@ +#include "nested_hello.hpp" + +int nested_hello() { + return 0; +} diff --git a/tests/test_component_set/src/main.cpp b/tests/test_component_set/src/main.cpp new file mode 100644 index 0000000..d9a704d --- /dev/null +++ b/tests/test_component_set/src/main.cpp @@ -0,0 +1,5 @@ +#include "nested_hello.hpp" + +int main() { + return nested_hello(); +} diff --git a/tests/test_interface/CMakeLists.txt b/tests/test_interface/CMakeLists.txt new file mode 100644 index 0000000..b64cc97 --- /dev/null +++ b/tests/test_interface/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) +project(TestInterface) + +include($ENV{ABCMAKE_PATH}/ab.cmake) +add_main_component(${PROJECT_NAME}) diff --git a/tests/test_interface/components/header_lib/CMakeLists.txt b/tests/test_interface/components/header_lib/CMakeLists.txt new file mode 100644 index 0000000..1c7bbad --- /dev/null +++ b/tests/test_interface/components/header_lib/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) +project(header_lib) + +include($ENV{ABCMAKE_PATH}/ab.cmake) +add_component(${PROJECT_NAME} INTERFACE) diff --git a/tests/test_interface/components/header_lib/include/header_lib.hpp b/tests/test_interface/components/header_lib/include/header_lib.hpp new file mode 100644 index 0000000..6941e81 --- /dev/null +++ b/tests/test_interface/components/header_lib/include/header_lib.hpp @@ -0,0 +1,5 @@ +#pragma once + +inline int get_value() { + return 42; +} diff --git a/tests/test_interface/src/main.cpp b/tests/test_interface/src/main.cpp new file mode 100644 index 0000000..2492e55 --- /dev/null +++ b/tests/test_interface/src/main.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + return get_value() == 42 ? 0 : 1; +} diff --git a/tests/test_interface_link/CMakeLists.txt b/tests/test_interface_link/CMakeLists.txt new file mode 100644 index 0000000..71c5eed --- /dev/null +++ b/tests/test_interface_link/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) +project(TestInterfaceLink) + +include($ENV{ABCMAKE_PATH}/ab.cmake) +add_main_component(${PROJECT_NAME}) diff --git a/tests/test_interface_link/components/base_lib/CMakeLists.txt b/tests/test_interface_link/components/base_lib/CMakeLists.txt new file mode 100644 index 0000000..7810788 --- /dev/null +++ b/tests/test_interface_link/components/base_lib/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.15) +project(base_lib) + +include($ENV{ABCMAKE_PATH}/ab.cmake) +add_component(${PROJECT_NAME}) diff --git a/tests/test_interface_link/components/base_lib/include/base_lib.hpp b/tests/test_interface_link/components/base_lib/include/base_lib.hpp new file mode 100644 index 0000000..7a2478e --- /dev/null +++ b/tests/test_interface_link/components/base_lib/include/base_lib.hpp @@ -0,0 +1,3 @@ +#pragma once + +int get_base_value(); diff --git a/tests/test_interface_link/components/base_lib/src/base_lib.cpp b/tests/test_interface_link/components/base_lib/src/base_lib.cpp new file mode 100644 index 0000000..16f18a5 --- /dev/null +++ b/tests/test_interface_link/components/base_lib/src/base_lib.cpp @@ -0,0 +1,5 @@ +#include "base_lib.hpp" + +int get_base_value() { + return 42; +} diff --git a/tests/test_interface_link/components/interface_lib/CMakeLists.txt b/tests/test_interface_link/components/interface_lib/CMakeLists.txt new file mode 100644 index 0000000..db713d9 --- /dev/null +++ b/tests/test_interface_link/components/interface_lib/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.15) +project(interface_lib) + +include($ENV{ABCMAKE_PATH}/ab.cmake) +add_component(${PROJECT_NAME} INTERFACE) +target_link_components(${PROJECT_NAME} PATH ${CMAKE_CURRENT_LIST_DIR}/../base_lib) diff --git a/tests/test_interface_link/components/interface_lib/include/interface_lib.hpp b/tests/test_interface_link/components/interface_lib/include/interface_lib.hpp new file mode 100644 index 0000000..5a4f115 --- /dev/null +++ b/tests/test_interface_link/components/interface_lib/include/interface_lib.hpp @@ -0,0 +1,6 @@ +#pragma once +#include "base_lib.hpp" + +inline int get_interface_value() { + return get_base_value() * 2; +} diff --git a/tests/test_interface_link/src/main.cpp b/tests/test_interface_link/src/main.cpp new file mode 100644 index 0000000..e98b3cc --- /dev/null +++ b/tests/test_interface_link/src/main.cpp @@ -0,0 +1,7 @@ +#include "interface_lib.hpp" +#include + +int main() { + std::cout << "Value: " << get_interface_value() << std::endl; + return 0; +} diff --git a/tests/test_project_build.py b/tests/test_project_build.py index f8e3797..b7d257a 100644 --- a/tests/test_project_build.py +++ b/tests/test_project_build.py @@ -34,6 +34,21 @@ def test_cmake_package(self): # and a non-abcmake CMake library (lib_exclamation). Ensures package auto-detection works. self.build_cmake("test_cmake_package") + def test_interface(self): + self.build_cmake("test_interface") + + def test_interface_link(self): + # Test INTERFACE library linking to other components via target_link_components + self.build_cmake("test_interface_link") + + def test_component_set(self): + # Component set registers nested components for name-based linking + self.build_cmake("test_component_set") + + def test_alias_link(self): + # Parent CMake (no abcmake) links abcmake component via namespaced alias + self.build_cmake("test_alias_link") + if __name__ == '__main__': unittest.main()