Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ scnlib = "1.1.2"

[profile]
definitions = ["BOOST_PROCESS_USE_STD_FS"]
ubsan = true
asan = true
tsan = false
leak = false

[target.'cfg(not(compiler = "msvc"))'.profile]
cxxflags = ["-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter", "-Wno-missing-field-initializers"]
Expand Down
10 changes: 9 additions & 1 deletion cppship.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cppship"
version = "0.6.0"
version = "0.7.0"
authors = []
std = 20

Expand All @@ -20,5 +20,13 @@ definitions = ["BOOST_PROCESS_USE_STD_FS"]
[target.'cfg(not(compiler = "msvc"))'.profile]
cxxflags = ["-Wall", "-Wextra", "-Werror", "-Wno-unused-parameter", "-Wno-missing-field-initializers"]

[target.'cfg(not(compiler = "msvc"))'.profile.debug]
ubsan = true

# macos with gcc11 has bugs in asan:
# `member call on address 0x60b0000001a0 which does not point to an object of type '_Sp_counted_base'`
[target.'cfg(any(os = "linux", compiler = "clang", compiler = "apple_clang"))'.profile.debug]
asan = true

[target.'cfg(compiler = "msvc")'.profile]
cxxflags = ["/Zc:__cplusplus", "/Zc:preprocessor", "/MP"]
3 changes: 3 additions & 0 deletions include/cppship/core/manifest.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class Manifest {
const ProfileOptions& default_profile() const { return mProfileDefault; }
const ProfileOptions& profile(Profile prof) const;

private:
void set_defaults_();

private:
std::string mName;
std::string mVersion;
Expand Down
6 changes: 6 additions & 0 deletions include/cppship/core/profile.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <optional>
#include <set>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -54,7 +55,12 @@ inline Profile parse_profile(std::string profile)

struct ProfileConfig {
std::vector<std::string> cxxflags;
std::vector<std::string> linkflags;
std::vector<std::string> definitions;
std::optional<bool> ubsan;
std::optional<bool> tsan;
std::optional<bool> asan;
std::optional<bool> leak;
};

struct ConditionConfig {
Expand Down
56 changes: 52 additions & 4 deletions lib/cmake/generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,11 @@ include(CPack)

namespace {

class ProfileOptionAppender {
class ProfileOptionGen {
std::ostream& mOut;

public:
ProfileOptionAppender(std::ostream& out)
ProfileOptionGen(std::ostream& out)
: mOut(out)
{
}
Expand All @@ -360,6 +360,30 @@ class ProfileOptionAppender {
for (const auto& def : config.definitions) {
mOut << fmt::format("{}add_compile_definitions($<$<CONFIG:{}>:{}>)\n", indent, profile, def);
}

if (config.ubsan.value_or(false)) {
mOut << fmt::format("{}add_compile_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=undefined");
mOut << fmt::format("{}add_link_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=undefined");
mOut << fmt::format("{}message(STATUS \"Enable ubsan\")\n", indent);
}

if (config.tsan.value_or(false)) {
mOut << fmt::format("{}add_compile_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=thread");
mOut << fmt::format("{}add_link_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=thread");
mOut << fmt::format("{}message(STATUS \"Enable tsan\")\n", indent);
}

if (config.asan.value_or(false)) {
mOut << fmt::format("{}add_compile_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=address");
mOut << fmt::format("{}add_link_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=address");
mOut << fmt::format("{}message(STATUS \"Enable asan\")\n", indent);
}

if (config.leak.value_or(false)) {
mOut << fmt::format("{}add_compile_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=leak");
mOut << fmt::format("{}add_link_options($<$<CONFIG:{}>:{}>)\n", indent, profile, "-fsanitize=leak");
mOut << fmt::format("{}message(STATUS \"Enable leak\")\n", indent);
}
}

void output(const ProfileConfig& config, std::string_view indent = "")
Expand All @@ -370,6 +394,30 @@ class ProfileOptionAppender {
for (const auto& def : config.definitions) {
mOut << fmt::format("{}add_compile_definitions({})\n", indent, def);
}

if (config.ubsan.value_or(false)) {
mOut << fmt::format("{}add_compile_options({})\n", indent, "-fsanitize=undefined");
mOut << fmt::format("{}add_link_options({})\n", indent, "-fsanitize=undefined");
mOut << fmt::format("{}message(STATUS \"Enable ubsan\")\n", indent);
}

if (config.tsan.value_or(false)) {
mOut << fmt::format("{}add_compile_options({})\n", indent, "-fsanitize=thread");
mOut << fmt::format("{}add_link_options({})\n", indent, "-fsanitize=thread");
mOut << fmt::format("{}message(STATUS \"Enable tsan\")\n", indent);
}

if (config.asan.value_or(false)) {
mOut << fmt::format("{}add_compile_options({})\n", indent, "-fsanitize=address");
mOut << fmt::format("{}add_link_options({})\n", indent, "-fsanitize=address");
mOut << fmt::format("{}message(STATUS \"Enable asan\")\n", indent);
}

if (config.leak.value_or(false)) {
mOut << fmt::format("{}add_compile_options({})\n", indent, "-fsanitize=leak");
mOut << fmt::format("{}add_link_options({})\n", indent, "-fsanitize=leak");
mOut << fmt::format("{}message(STATUS \"Enable leak\")\n", indent);
}
}
};

Expand All @@ -379,7 +427,7 @@ void CmakeGenerator::fill_default_profile_()
{
const auto& default_profile = mManifest.default_profile();

ProfileOptionAppender appender(mOut);
ProfileOptionGen appender(mOut);
appender.output(default_profile.config);

for (const auto& [condition, config] : default_profile.conditional_configs) {
Expand All @@ -394,7 +442,7 @@ void CmakeGenerator::fill_profile_(Profile profile)
const auto& options = mManifest.profile(profile);
const auto& profile_str = to_string(profile);

ProfileOptionAppender appender(mOut);
ProfileOptionGen appender(mOut);
appender.output(profile_str, options.config);

for (const auto& [condition, config] : options.conditional_configs) {
Expand Down
62 changes: 60 additions & 2 deletions lib/core/manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
#include "cppship/util/fs.h"
#include "cppship/util/io.h"

#include <array>
#include <cstdlib>
#include <set>

#include <boost/algorithm/string.hpp>
#include <fmt/os.h>
#include <range/v3/algorithm/any_of.hpp>
#include <range/v3/view/concat.hpp>
#include <toml.hpp>

using namespace cppship;
using namespace ranges;

namespace {

Expand All @@ -24,6 +27,20 @@ template <class T = toml::value> T get(const toml::value& value, const std::stri
return toml::find<T>(value, key);
}

std::optional<bool> get_bool(const toml::value& value, const std::string& key)
{
if (value.is_uninitialized() || !value.contains(key)) {
return std::nullopt;
}

const auto& content = value.at(key);
if (!content.is_boolean()) {
throw Error { fmt::format("invalid manifest: {} should be a bool", key) };
}

return content.as_boolean();
}

std::vector<std::string> get_list(const toml::value& value, const std::string& key)
{
if (value.is_uninitialized() || !value.contains(key)) {
Expand Down Expand Up @@ -129,7 +146,7 @@ void check_dependency_dups(const std::vector<DeclaredDependency>& deps, const st
{
std::set<std::string> package_seen;

for (const auto& dep : ranges::views::concat(deps, dev_deps)) {
for (const auto& dep : views::concat(deps, dev_deps)) {
if (package_seen.contains(dep.package)) {
throw Error { fmt::format("package {} already declared", dep.package) };
}
Expand All @@ -142,10 +159,27 @@ ProfileConfig parse_profile_options(const toml::value& manifest, const std::stri
{
const auto profile = toml::find_or(manifest, key, {});

return {
ProfileConfig config = {
.cxxflags = get_list(profile, "cxxflags"),
.linkflags = get_list(profile, "linkflags"),
.definitions = get_list(profile, "definitions"),
.ubsan = get_bool(profile, "ubsan"),
.tsan = get_bool(profile, "tsan"),
.asan = get_bool(profile, "asan"),
.leak = get_bool(profile, "leak"),
};

if (config.tsan && *config.tsan) {
if (config.asan && *config.asan) {
throw Error { "tsan cannot be used with asan" };
}

if (config.leak && *config.leak) {
throw Error { "tsan cannot be used with leak" };
}
}

return config;
}

}
Expand Down Expand Up @@ -204,13 +238,37 @@ Manifest::Manifest(const fs::path& file)
});
}
}

// set defaults
set_defaults_();
} catch (const std::out_of_range& e) {
throw Error { e.what() };
} catch (const toml::exception& e) {
throw Error { fmt::format("invalid manifest format at {}", e.what()) };
}
}

void Manifest::set_defaults_()
{
// TODO
/*
const bool ubsan_present
= any_of(std::array { &mProfileDebug, &mProfileDebug, &mProfileRelease }, [](const ProfileOptions* profile) {
return profile->config.ubsan.has_value()
|| any_of(profile->conditional_configs, [](const auto& cc) { return cc.config.ubsan.has_value(); });
});
if (ubsan_present) {
return;
}

// in debug profile, enable ubsan if not msvc
mProfileDebug.conditional_configs.push_back({
.condition = core::CfgNot { { core::cfg::Compiler::msvc } },
.config = { .ubsan = true },
});
*/
}

const ProfileOptions& Manifest::profile(Profile prof) const
{
switch (prof) {
Expand Down