Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
h-mathias committed Aug 8, 2024
0 parents commit a34963d
Show file tree
Hide file tree
Showing 29 changed files with 3,554 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: Swatinem/rust-cache@v2
- name: clippy
run: cargo clippy
- name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v2
with:
version: "17.0"
- name: Install cmake + ninja
run: sudo apt-get install -y cmake ninja-build
- name: tests
run: cargo test
- name: slow tests
run: cargo test -- --ignored
- name: docs
run: cargo doc
27 changes: 27 additions & 0 deletions .github/workflows/release-plz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Release-plz

permissions:
pull-requests: write
contents: write

on:
push:
branches:
- main

jobs:
release-plz:
name: Release-plz
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: MarcoIeni/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

Cargo.lock

# CMake build directory created by running the basic example without args
examples/cpp/build/
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog
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.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.0.9](https://github.com/h-mathias/cmake-file-api-rs/releases/tag/v0.0.9) - 2024-08-08

### Other
- Initial commit
32 changes: 32 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "cmake-file-api"
version = "0.1.0"
authors = ["Mathias H. <[email protected]>"]
description = "Parsing and interacting with cmake-file-api"
homepage = "https://github.com/h-mathias/cmake-file-api-rs"
repository = "https://github.com/h-mathias/cmake-file-api-rs"
readme = "README.md"
keywords = ["cmake", "cmake-file-api", "cpp"]
categories = ["api-bindings", "filesystem"]
license = "MIT"
edition = "2021"

[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
shlex = "1.3"

[dev-dependencies]
tempdir = "0.3"

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
shadow_reuse = "forbid"
exhaustive_enums = "forbid"
panic = "forbid"
shadow_unrelated = "forbid"


21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 h-mathias

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
![GitHub Workflow Status](https://github.com/h-mathias/cmake-file-api-rs/actions/workflows/ci.yaml/badge.svg)

cmake-file-api-rs
=======

Library for interacting with the [cmake-file-api](https://cmake.org/cmake/help/latest/manual/cmake-file-api.7.html)
- Writing queries
- Reading replies

Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org).

### Usage

Run `cargo add cmake-file-api` to add the crate to your project.

### Example

Build query and parse cmake-file-api:

```rust
use cmake_file_api::{objects, query, reply};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let source_dir = std::path::Path::new("path/to/source/dir");
let build_dir = std::path::Path::new("path/to/build/dir");

// write query for codemodel-v2
query::Writer::default()
.request_object::<objects::CodeModelV2>()
.write_stateless(build_dir)?;

// run cmake
assert!(std::process::Command::new("cmake")
.arg("-S")
.arg(source_dir)
.arg("-B")
.arg(build_dir)
.status()?
.success());

// parse cmake-file-api
let reader = reply::Reader::from_build_dir(build_dir)?;

// read and print codemodel-v2
let codemodel: objects::CodeModelV2 = reader.read_object()?;
codemodel.configurations.iter().for_each(|config| {
config.targets.iter().for_each(|target| {
println!("{}", target.name);
println!("{:#?}", target.sources);
});
});

Ok(())
}
```

# CMake-file-api
The `cmake-file-api` is the predecessor of the `cmaker-server` and was introduced in `CMake` 3.14. It provides a rich interface for querying configuration and project information.
The API is versioned, and the current version is v1. As the name suggests, the API is based on files, which are written to disk by `CMake` and read by client tools. `CMake` generates these files in a directory named `.cmake/api/v1` in the build directory.
The V1 API is a collection of JSON files that describe the configuration of the `CMake` project, and it always contains an `index-*.json` file which lists all available objects.
The objects are also versioned on their own, e.g. `codemodel-v2.json`. `CMake` will generate the files on demand,
and expects clients to first write queries inside `.cmake/api/v1/query` before configuration.
The query describes which objects the client is interested in. With stateful queries, the client can also provide additional client data which is available in the reply.
The API is commonly used insides IDE's but can also be used for other tooling purposes like invoking tools which need compile flags.

# Related projects
- [python-cmake-file-api](https://github.com/madebr/python-cmake-file-api): Python bindings for the CMake File API
- [cfi-java](https://github.com/WalkerKnapp/cfi-java): Java bindings for the CMake File API
24 changes: 24 additions & 0 deletions UNLICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>
95 changes: 95 additions & 0 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use cmake_file_api::{objects, reply};
use std::path::PathBuf;
use std::process::ExitCode;

/// This example demonstrates how to use `cmake_file_api` to get information about a `CMake` project.
fn main() -> Result<ExitCode, Box<dyn std::error::Error>> {
// source directory from argument or default to examples/cpp
let source_dir = if let Some(arg) = std::env::args().nth(1) {
PathBuf::from(arg)
} else {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join("cpp")
};

// build directory from argument or default to examples/cpp/build
let build_dir = if let Some(arg) = std::env::args().nth(2) {
PathBuf::from(arg)
} else {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("examples")
.join("cpp")
.join("build")
};

if !source_dir.exists() {
println!("Source directory does not exist: {}", source_dir.display());
return Ok(ExitCode::FAILURE);
}

if !build_dir.exists() {
println!("Creating build directory: {}", build_dir.display());
std::fs::create_dir_all(&build_dir).unwrap();
}

if !reply::is_available(&build_dir) {
println!("CMake File API is not available, generating it now");

println!("Writing CMake File API query");
cmake_file_api::query::Writer::default()
.request_object::<objects::CodeModelV2>()
.write_stateless(&build_dir)?;

// run cmake
println!("Running cmake");
assert!(std::process::Command::new("cmake")
.arg("-S")
.arg(&source_dir)
.arg("-B")
.arg(&build_dir)
.status()?
.success());
}

// load the file api
println!("Loading CMake File API");
let reader = reply::Reader::from_build_dir(&build_dir)?;

// get the codemodel
let codemodel: objects::CodeModelV2 = reader.read_object()?;

// print all source files and their compile flags
for target in &codemodel.configurations[0].targets {
if target.sources.is_empty() {
continue;
}

println!("Source files for target {}:", target.name);
for source in &target.sources {
println!(" {}", source.path.display());

if let Some(compile_group) = &source
.compile_group_index
.and_then(|i| target.compile_groups.get(i))
{
println!(" Includes:");
for include in &compile_group.includes {
println!(" * {}", include.path.display());
}

println!(" Defines:");
for define in compile_group.defines() {
println!(" * {define}");
}

println!(" Flags:");
for flag in compile_group.flags() {
println!(" * {flag}");
}
}
}
}

Ok(ExitCode::SUCCESS)
}
5 changes: 5 additions & 0 deletions examples/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

cmake_minimum_required(VERSION 3.21)
project(cmake-file-api-example LANGUAGES CXX)

add_executable(cmake-file-api-example main.cpp)
5 changes: 5 additions & 0 deletions examples/cpp/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


int main(int argc, char **argv) {
return 0;
}
34 changes: 34 additions & 0 deletions examples/example_from_readme.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use cmake_file_api::{objects, query, reply};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let source_dir = std::path::Path::new(".");
let build_dir = std::path::Path::new(".");

// write query for codemodel-v2
query::Writer::default()
.request_object::<objects::CodeModelV2>()
.write_stateless(build_dir)?;

// run cmake
assert!(std::process::Command::new("cmake")
.arg("-S")
.arg(source_dir)
.arg("-B")
.arg(build_dir)
.status()?
.success());

// parse cmake-file-api
let reader = reply::Reader::from_build_dir(build_dir)?;

// read and print codemodel-v2
let codemodel: objects::CodeModelV2 = reader.read_object()?;
codemodel.configurations.iter().for_each(|config| {
config.targets.iter().for_each(|target| {
println!("{}", target.name);
println!("{:#?}", target.sources);
});
});

Ok(())
}
Loading

0 comments on commit a34963d

Please sign in to comment.