Skip to content

Commit b566c4f

Browse files
committed
Add a Rust project that exposes the C++ CProver API in Rust.
At the moment this supports the capacity for * loading a goto_model from files * verifying the goto_model * performing a sample instrumentation pass
1 parent ff03dd5 commit b566c4f

File tree

14 files changed

+538
-9
lines changed

14 files changed

+538
-9
lines changed

CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,32 @@ if(WITH_JBMC)
256256
add_subdirectory(jbmc)
257257
endif()
258258

259+
option(WITH_RUST_API "Build with the CPROVER Rust API" ON)
260+
if(${CMAKE_VERSION} VERSION_LESS "3.19.0")
261+
message("Unable to build the Rust API without version CMake 3.19.0")
262+
message("(The Rust build depends on CMake plugins with dependencies on CMake 3.19.0 and above)")
263+
else()
264+
if(WITH_RUST_API)
265+
include(FetchContent)
266+
267+
FetchContent_Declare(
268+
Corrosion
269+
GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git
270+
GIT_TAG v0.3.1
271+
)
272+
273+
FetchContent_MakeAvailable(Corrosion)
274+
275+
corrosion_import_crate(MANIFEST_PATH src/libcprover-rust/Cargo.toml)
276+
corrosion_set_env_vars(cprover-api-rust CBMC_LIB_DIR=${CMAKE_LIBRARY_OUTPUT_DIRECTORY} SAT_IMPL=${sat_impl})
277+
corrosion_link_libraries(cprover-api-rust cprover-api-cpp)
278+
install(TARGETS cprover-api-rust RUNTIME DESTINATION lib)
279+
# NOTE: We want to rename to a name consistent with the name of the
280+
# rest of the static libraries built as a result of a build. The reason
281+
# we cannot set the target name directly is that Cargo doesn't support
282+
# hyphens in names of packages, so it builds the name by default with
283+
# an underscore.
284+
endif()
285+
endif()
286+
259287
include(cmake/packaging.cmake)

src/libcprover-cpp/CMakeLists.txt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,21 @@ file(GLOB_RECURSE sources "*.cpp" "*.h")
33
# This step builds the API in the form of a statically linked library (libcprover-api-cpp.a)
44
add_library(cprover-api-cpp ${sources})
55
generic_includes(cprover-api-cpp)
6-
target_link_libraries(cprover-api-cpp goto-checker goto-programs util langapi ansi-c)
6+
target_link_libraries(cprover-api-cpp
7+
goto-checker
8+
goto-programs
9+
util
10+
langapi
11+
ansi-c
12+
json-symtab-language
13+
solvers
14+
xml
15+
cpp
16+
cbmc-lib
17+
analyses
18+
statement-list
19+
linking
20+
pointer-analysis
21+
goto-instrument-lib
22+
goto-analyzer-lib
23+
goto-cc-lib)

src/libcprover-cpp/api.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131

3232
extern configt config;
3333

34+
std::unique_ptr<std::string> api_sessiont::get_api_version() const
35+
{
36+
return util_make_unique<std::string>(std::string{"0.1"});
37+
}
38+
3439
struct api_session_implementationt
3540
{
3641
std::unique_ptr<goto_modelt> model;
@@ -112,13 +117,14 @@ void api_sessiont::set_message_callback(
112117
util_make_unique<api_message_handlert>(callback, context);
113118
}
114119

115-
void api_sessiont::load_model_from_files(const std::vector<std::string> &files)
120+
void api_sessiont::load_model_from_files(
121+
const std::vector<std::string> &files) const
116122
{
117123
implementation->model = util_make_unique<goto_modelt>(initialize_goto_model(
118124
files, *implementation->message_handler, *implementation->options));
119125
}
120126

121-
void api_sessiont::verify_model()
127+
void api_sessiont::verify_model() const
122128
{
123129
PRECONDITION(implementation->model);
124130

@@ -162,7 +168,7 @@ void api_sessiont::verify_model()
162168
verifier.report();
163169
}
164170

165-
void api_sessiont::drop_unused_functions()
171+
void api_sessiont::drop_unused_functions() const
166172
{
167173
INVARIANT(
168174
implementation->model != nullptr,
@@ -177,7 +183,7 @@ void api_sessiont::drop_unused_functions()
177183
*implementation->model, *implementation->message_handler);
178184
}
179185

180-
void api_sessiont::validate_goto_model()
186+
void api_sessiont::validate_goto_model() const
181187
{
182188
INVARIANT(
183189
implementation->model != nullptr,

src/libcprover-cpp/api.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,19 @@ struct api_sessiont
6666

6767
/// Load a goto_model from a given vector of filenames.
6868
/// \param files: A vector<string> containing the filenames to be loaded
69-
void load_model_from_files(const std::vector<std::string> &files);
69+
void load_model_from_files(const std::vector<std::string> &files) const;
7070

7171
/// Verify previously loaded model.
72-
void verify_model();
72+
void verify_model() const;
7373

7474
/// Drop unused functions from the loaded goto_model simplifying it
75-
void drop_unused_functions();
75+
void drop_unused_functions() const;
7676

7777
/// Validate the loaded goto model
78-
void validate_goto_model();
78+
void validate_goto_model() const;
79+
80+
// A simple API version information function.
81+
std::unique_ptr<std::string> get_api_version() const;
7982

8083
private:
8184
std::unique_ptr<api_session_implementationt> implementation;

src/libcprover-rust/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ignore build artefacts folder
2+
target/

src/libcprover-rust/Cargo.lock

Lines changed: 173 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/libcprover-rust/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "cprover-api-rust"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
cxx = "1.0"
10+
11+
[build-dependencies]
12+
cxx-build = "1.0"
13+
14+
[lib]
15+
crate-type = ["staticlib"]

src/libcprover-rust/build.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use std::env;
2+
use std::io::{Error, ErrorKind};
3+
use std::path::Path;
4+
use std::path::PathBuf;
5+
6+
fn get_current_working_dir() -> std::io::Result<PathBuf> {
7+
env::current_dir()
8+
}
9+
10+
fn get_library_build_dir() -> std::io::Result<PathBuf> {
11+
let env_var_name = "CBMC_LIB_DIR";
12+
let env_var_fetch_result = env::var(env_var_name);
13+
if let Ok(lib_dir) = env_var_fetch_result {
14+
let mut path = PathBuf::new();
15+
path.push(lib_dir);
16+
return Ok(path);
17+
}
18+
Err(Error::new(ErrorKind::Other, "failed to get library output directory"))
19+
}
20+
21+
fn get_sat_library() -> &'static str {
22+
let env_var_name = "SAT_IMPL";
23+
let env_var_fetch_result = env::var(env_var_name);
24+
if let Ok(sat_impl) = env_var_fetch_result {
25+
let solver_lib = match sat_impl.as_str() {
26+
"minisat2" => "minisat2-condensed",
27+
"glucose" => "libglucose-condensed",
28+
"cadical" => "cadical",
29+
_ => "Error: no identifiable solver detected"
30+
};
31+
return solver_lib;
32+
}
33+
"Error: no identifiable solver detected"
34+
}
35+
36+
fn main() {
37+
let cbmc_source_path = Path::new("..");
38+
let cpp_api_path = Path::new("../libcprover-cpp/");
39+
let _build = cxx_build::bridge("src/lib.rs")
40+
.include(cbmc_source_path)
41+
.include(cpp_api_path)
42+
.include(get_current_working_dir().unwrap())
43+
.file("src/c_api.cc")
44+
.flag_if_supported("-std=c++11")
45+
.compile("cprover-rust-api");
46+
47+
println!("cargo:rerun-if-changed=src/c_api.cc");
48+
println!("cargo:rerun-if-changed=include/c_api.h");
49+
50+
let libraries_path = match get_library_build_dir() {
51+
Ok(path) => path,
52+
Err(err) => panic!("{}", err)
53+
};
54+
55+
println!("cargo:rustc-link-search=native={}", libraries_path.display());
56+
println!("cargo:rustc-link-lib=static=goto-programs");
57+
println!("cargo:rustc-link-lib=static=util");
58+
println!("cargo:rustc-link-lib=static=langapi");
59+
println!("cargo:rustc-link-lib=static=ansi-c");
60+
println!("cargo:rustc-link-lib=static=analyses");
61+
println!("cargo:rustc-link-lib=static=goto-instrument-lib");
62+
println!("cargo:rustc-link-lib=static=big-int");
63+
println!("cargo:rustc-link-lib=static=linking");
64+
println!("cargo:rustc-link-lib=static=goto-checker");
65+
println!("cargo:rustc-link-lib=static=solvers");
66+
println!("cargo:rustc-link-lib=static=assembler");
67+
println!("cargo:rustc-link-lib=static=xml");
68+
println!("cargo:rustc-link-lib=static=json");
69+
println!("cargo:rustc-link-lib=static=json-symtab-language");
70+
println!("cargo:rustc-link-lib=static=cpp");
71+
println!("cargo:rustc-link-lib=static=jsil");
72+
println!("cargo:rustc-link-lib=static=statement-list");
73+
println!("cargo:rustc-link-lib=static=goto-symex");
74+
println!("cargo:rustc-link-lib=static=pointer-analysis");
75+
println!("cargo:rustc-link-lib=static={}", get_sat_library());
76+
println!("cargo:rustc-link-lib=static=cbmc-lib");
77+
println!("cargo:rustc-link-lib=static=cprover-api-cpp");
78+
}

0 commit comments

Comments
 (0)