From ae66a118b06db477c3474e0bb98ab9b58a5d4c6b Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 8 May 2026 17:00:22 -0700 Subject: [PATCH 1/5] feat: specify compiler in build.cpp2 --- build.cmd | 6 +- build.cpp2 | 4 + share/cpp2b/cpp2b.build.cppm.tpl | 1 + src/cpp2b_build_info_parser.cppm | 152 +++++++++++++++++++++++++++++++ src/main.cpp2 | 86 +++++++++-------- 5 files changed, 209 insertions(+), 40 deletions(-) diff --git a/build.cmd b/build.cmd index 39965a2..c6b8d7e 100644 --- a/build.cmd +++ b/build.cmd @@ -118,7 +118,7 @@ cl /nologo ^ /std:c++latest /W4 /MDd /EHsc ^ /reference "%modules_dir%\std.ifc" ^ /reference "%modules_dir%\std.compat.ifc" ^ - /c /interface /TP "%root_dir%src\dylib.cppm" > NUL + /c /interface /TP "%root_dir%src\dylib.cppm" popd if %ERRORLEVEL% neq 0 ( @@ -132,7 +132,7 @@ cl /nologo ^ /std:c++latest /W4 /MDd /EHsc ^ /reference "%modules_dir%\std.ifc" ^ /reference "%modules_dir%\std.compat.ifc" ^ - /c /interface /TP "%root_dir%src\nlohmann.json.cppm" > NUL + /c /interface /TP "%root_dir%src\nlohmann.json.cppm" popd if %ERRORLEVEL% neq 0 ( @@ -147,7 +147,7 @@ cl /nologo ^ -I"%cppfront_include_dir%" ^ /reference "%modules_dir%\std.ifc" ^ /reference "%modules_dir%\std.compat.ifc" ^ - /c /interface /TP "%root_dir%src\cpp2b_build_info_parser.cppm" > NUL + /c /interface /TP "%root_dir%src\cpp2b_build_info_parser.cppm" popd if %ERRORLEVEL% neq 0 ( diff --git a/build.cpp2 b/build.cpp2 index d2baad7..7ebf6a5 100644 --- a/build.cpp2 +++ b/build.cpp2 @@ -1,6 +1,10 @@ import cpp2b.build; cppfront :== "v0.8.1"; +compiler :== inspect cpp2b::host_platform() -> cpp2b::compiler_type { + is cpp2b::platform::windows = cpp2b::compiler_type::msvc; + is _ = cpp2b::compiler_type::clang; +}; build: (inout b: cpp2b::build) -> void = { _ = b.cpp1_module().source_path("src/dylib.cppm"); diff --git a/share/cpp2b/cpp2b.build.cppm.tpl b/share/cpp2b/cpp2b.build.cppm.tpl index d7bab7f..4c06d9a 100644 --- a/share/cpp2b/cpp2b.build.cppm.tpl +++ b/share/cpp2b/cpp2b.build.cppm.tpl @@ -9,6 +9,7 @@ module; export module cpp2b.build; import std; +export import cpp2b; struct cpp2b_detail_build_impl; struct cpp2b_detail_git_repo_impl; diff --git a/src/cpp2b_build_info_parser.cppm b/src/cpp2b_build_info_parser.cppm index f793d9c..efea8a4 100644 --- a/src/cpp2b_build_info_parser.cppm +++ b/src/cpp2b_build_info_parser.cppm @@ -63,6 +63,158 @@ source_info parse_source(const std::string& filename) { result.kind = source_kind::build; // Extract constants for(size_t j = 0; j < all_tokens.size(); ++j) { + auto name = all_tokens[j].as_string_view(); + + // Specific handling for 'compiler :== ...' + if (name == "compiler" && j + 2 < all_tokens.size() && + all_tokens[j+1].type() == cpp2::lexeme::Colon && + all_tokens[j+2].type() == cpp2::lexeme::EqualComparison) { + + size_t k = j + 3; + auto match_path = [&](const std::vector& parts) { + size_t cur = k; + for (size_t i = 0; i < parts.size(); ++i) { + if (cur >= all_tokens.size()) return false; + if (all_tokens[cur].as_string_view() != parts[i]) return false; + cur++; + if (i < parts.size() - 1) { + if (cur >= all_tokens.size()) return false; + if (all_tokens[cur].as_string_view() == "::") { + cur++; + } else if (cur + 1 < all_tokens.size() && + all_tokens[cur].as_string_view() == ":" && + all_tokens[cur+1].as_string_view() == ":") { + cur += 2; + } else { + return false; + } + } + } + k = cur; + return true; + }; + + // Case 1: compiler :== cpp2b::compiler_type::; + if (match_path({"cpp2b", "compiler_type"})) { + bool has_scope = false; + if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { + k++; + has_scope = true; + } else if (k + 1 < all_tokens.size() && + all_tokens[k].as_string_view() == ":" && + all_tokens[k+1].as_string_view() == ":") { + k += 2; + has_scope = true; + } + + if (has_scope && k < all_tokens.size() && + all_tokens[k].type() == cpp2::lexeme::Identifier) { + result.constants["compiler"] = std::string(all_tokens[k].as_string_view()); + j = k; + continue; + } + } + // Case 2: compiler :== inspect cpp2b::host_platform() { ... } + else if (all_tokens[k].as_string_view() == "inspect") { + k++; + if (match_path({"cpp2b", "host_platform"})) { + if (k + 1 < all_tokens.size() && + all_tokens[k].as_string_view() == "(" && + all_tokens[k+1].as_string_view() == ")") { + + k += 2; + // Skip optional '-> result_type' + if (k < all_tokens.size() && + (all_tokens[k].as_string_view() == "->" || all_tokens[k].type() == cpp2::lexeme::Arrow)) { + k++; + while (k < all_tokens.size() && all_tokens[k].as_string_view() != "{") k++; + } + + if (k < all_tokens.size() && all_tokens[k].as_string_view() == "{") { + k += 1; + std::string_view host_platform = + + #if defined(_WIN32) + "windows"; + #elif defined(__APPLE__) + "macos"; + #else + "linux"; + #endif + + bool found = false; + while (k < all_tokens.size() && all_tokens[k].as_string_view() != "}") { + // is cpp2b::platform:: = cpp2b::compiler_type::; + // or is _ = ... + if (all_tokens[k].as_string_view() == "is") { + k++; + bool is_default = (all_tokens[k].as_string_view() == "_"); + std::string_view branch_platform = ""; + + if (!is_default) { + size_t saved_k_branch = k; + if (match_path({"cpp2b", "platform"})) { + bool has_scope = false; + if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { + k++; + has_scope = true; + } else if (k + 1 < all_tokens.size() && + all_tokens[k].as_string_view() == ":" && + all_tokens[k+1].as_string_view() == ":") { + k += 2; + has_scope = true; + } + + if (has_scope && k < all_tokens.size() && + (all_tokens[k].type() == cpp2::lexeme::Identifier || all_tokens[k].type() == cpp2::lexeme::Keyword)) { + branch_platform = all_tokens[k].as_string_view(); + k++; + } + } + } else { + k++; + } + + if (k < all_tokens.size() && all_tokens[k].as_string_view() == "=") { + k++; + if (match_path({"cpp2b", "compiler_type"})) { + bool has_scope = false; + if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { + k++; + has_scope = true; + } else if (k + 1 < all_tokens.size() && + all_tokens[k].as_string_view() == ":" && + all_tokens[k+1].as_string_view() == ":") { + k += 2; + has_scope = true; + } + + if (has_scope && k < all_tokens.size() && + (all_tokens[k].type() == cpp2::lexeme::Identifier || all_tokens[k].type() == cpp2::lexeme::Keyword)) { + + if (!found && (is_default || branch_platform == host_platform)) { + result.constants["compiler"] = std::string(all_tokens[k].as_string_view()); + found = true; + } + k++; + } + } + } + + } else { + k++; + } + } + if (found) { + j = k; + continue; + } + } + } + } + } + } + // identifier == string_literal ; if(j + 3 < all_tokens.size() && all_tokens[j].type() == cpp2::lexeme::Identifier && diff --git a/src/main.cpp2 b/src/main.cpp2 index 3abdfeb..907a5af 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -172,7 +172,7 @@ bmi_extension: (compiler: cpp2b::compiler_type) -> std::string_view = { std::exit(1); } -generate_cpp2b_module: () = { +generate_cpp2b_module: (compiler: cpp2b::compiler_type) = { using std::string_literals::_; cpp2b_module_template_path: fs::path = share_dir() / "cpp2b.cppm.tpl"s; @@ -183,7 +183,7 @@ generate_cpp2b_module: () = { std::exit(1); } - cpp2b_module_source_path: fs::path = ".cache/cpp2/source/_build/cpp2b(module_source_extension(cpp2b::compiler()))$"; + cpp2b_module_source_path: fs::path = ".cache/cpp2/source/_build/cpp2b(module_source_extension(compiler))$"; cpp2b_module_source_stream: std::ofstream = (cpp2b_module_source_path); if !cpp2b_module_source_stream { @@ -214,14 +214,14 @@ generate_cpp2b_module: () = { impl: cpp2b_detail_cpp1_module_impl = (); impl.source_path = cpp2b_module_source_path; - build_cpp1_module(impl, "cpp2b", :std::vector=("std", "std.compat")); + build_cpp1_module(impl, "cpp2b", :std::vector=("std", "std.compat"), compiler); } -generate_cpp2b_build_module: () = { +generate_cpp2b_build_module: (compiler: cpp2b::compiler_type) = { using std::string_literals::_; cpp2b_module_template_path: fs::path = share_dir() / "cpp2b.build.cppm.tpl"s; - cpp2b_module_source_path: fs::path = ".cache/cpp2/source/_build/cpp2b.build(module_source_extension(cpp2b::compiler()))$"; + cpp2b_module_source_path: fs::path = ".cache/cpp2/source/_build/cpp2b.build(module_source_extension(compiler))$"; cpp2b_module_source_stream: std::ofstream = (cpp2b_module_source_path); cpp2b_module_template_stream: std::ifstream = (cpp2b_module_template_path); @@ -251,7 +251,7 @@ generate_cpp2b_build_module: () = { impl: cpp2b_detail_cpp1_module_impl = (); impl.source_path = cpp2b_module_source_path; - build_cpp1_module(impl, "cpp2b.build", :std::vector=("std", "std.compat")); + build_cpp1_module(impl, "cpp2b.build", :std::vector=("std", "std.compat"), compiler); } get_vs_tools_dir: () -> fs::path = { @@ -271,25 +271,25 @@ get_system_modules_dir: (compiler: cpp2b::compiler_type) -> fs::path = { std::exit(1); } -ensure_system_module: (name: std::string) = { - ensure_system_module(name, :std::vector = ()); +ensure_system_module: (name: std::string, compiler: cpp2b::compiler_type) = { + ensure_system_module(name, :std::vector = (), compiler); } -ensure_system_module: (name: std::string, deps) = { +ensure_system_module: (name: std::string, deps, compiler: cpp2b::compiler_type) = { d := modules_dir(); - bmi := d / std::format("{}{}", name, bmi_extension(cpp2b::compiler())); - system_modules_dir := get_system_modules_dir(cpp2b::compiler()); + bmi := d / std::format("{}{}", name, bmi_extension(compiler)); + system_modules_dir := get_system_modules_dir(compiler); if !fs::exists(bmi) { impl: cpp2b_detail_cpp1_module_impl = (); - impl.source_path = system_modules_dir / std::format("{}{}", name, module_source_extension(cpp2b::compiler())); - build_cpp1_module(impl, name, deps); + impl.source_path = system_modules_dir / std::format("{}{}", name, module_source_extension(compiler)); + build_cpp1_module(impl, name, deps, compiler); } } -ensure_std_modules: () = { - ensure_system_module("std"); - ensure_system_module("std.compat", :std::vector=("std")); +ensure_std_modules: (compiler: cpp2b::compiler_type) = { + ensure_system_module("std", compiler); + ensure_system_module("std.compat", :std::vector=("std"), compiler); } @@ -335,8 +335,7 @@ unix_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, compiler_cmd: return cmd_str; } -build_cpp1_module: (impl: cpp2b_detail_cpp1_module_impl, name: std::string, module_deps) = { - compiler :== cpp2b::compiler(); +build_cpp1_module: (impl: cpp2b_detail_cpp1_module_impl, name: std::string, module_deps, compiler: cpp2b::compiler_type) = { d := fs::absolute(modules_dir()); bmi := d / std::format("{}{}", name, bmi_extension(compiler)); log_path := fs::path(".cache") / "cpp2" / "log" / "build" / ("(name)$.log"); @@ -629,22 +628,22 @@ cl_compile_cppfront: (cppfront_source: fs::path, out_cppfront_binary: fs::path, return exit_code; } -ensure_cppfront: (cppfront_source: fs::path) -> int = { +ensure_cppfront: (cppfront_source: fs::path, compiler: cpp2b::compiler_type) -> int = { cppfront: fs::path = ".cache/cpp2/tools/cppfront(executable_extension())$"; if !fs::exists(cppfront) { log_info("compiling cppfront..."); cppfront_compile_log_path: fs::path = ".cache/cpp2/log/cppfront.log"; - if cpp2b::compiler() == cpp2b::compiler_type::clang { + if compiler == cpp2b::compiler_type::clang { return unix_compile_cppfront("clang-19", fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); } - if cpp2b::compiler() == cpp2b::compiler_type::gcc { + if compiler == cpp2b::compiler_type::gcc { return unix_compile_cppfront("gcc", fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); } - if cpp2b::compiler() == cpp2b::compiler_type::msvc { + if compiler == cpp2b::compiler_type::msvc { return cl_compile_cppfront(fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); } @@ -673,16 +672,14 @@ find_git_root: (copy dir: fs::path) -> std::optional = { } find_root_dir: (copy dir: fs::path) -> std::optional = { - root_dir: std::optional = (); - while !dir.empty() { - if fs::exists(dir / "build.cpp2") { root_dir = dir; } + if fs::exists(dir / "build.cpp2") { return dir; } parent_dir := dir.parent_path(); if parent_dir == dir { break; } dir = parent_dir; } - return root_dir; + return std::nullopt; } find_build_cpp2_dir: (copy dir: fs::path) -> std::optional = { @@ -747,8 +744,7 @@ unix_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_inf } -build_binary: (info: cpp2b_source_binary_info, cppfront_include_dir: fs::path) -> build_binary_result = { - compiler :== cpp2b::compiler(); +build_binary: (info: cpp2b_source_binary_info, cppfront_include_dir: fs::path, compiler: cpp2b::compiler_type) -> build_binary_result = { bin_basename: fs::path = info.name(); if bin_basename.extension().empty() { bin_basename.replace_extension(executable_extension()); @@ -964,8 +960,7 @@ unix_build_build_script_cmd: (compiler_cmd: std::string, info: cpp2b_source_buil } -build_build_script: (info: cpp2b_source_build_info, cppfront_include_dir: fs::path) -> build_binary_result = { - compiler :== cpp2b::compiler(); +build_build_script: (info: cpp2b_source_build_info, cppfront_include_dir: fs::path, compiler: cpp2b::compiler_type) -> build_binary_result = { bin_outpath := fs::absolute(".cache/cpp2/bin") / fs::path(info.src).replace_extension(shared_library_extension()); log_path := fs::absolute(".cache/cpp2/log/compile") / fs::path(info.src).replace_extension(".log"); ensure_dir(log_path.parent_path()); @@ -1009,6 +1004,7 @@ full_build_info: @struct type = { build_scripts: std::vector = (); unknowns: std::vector = (); cppfront_include_dir: fs::path = (); + active_compiler: cpp2b::compiler_type = cpp2b::compiler(); bin_results: std::vector = (); @@ -1114,12 +1110,21 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c assert(build_info_variant.is_build()); build_info := build_info_variant.build(); + if build_info.constants.contains("cppfront_url") { cppfront_url = build_info.constants.at("cppfront_url"); } if build_info.constants.contains("cppfront") { cppfront = build_info.constants.at("cppfront"); } + if build_info.constants.contains("compiler") { + c_str := build_info.constants.at("compiler"); + if c_str == "msvc" { stuff.active_compiler = cpp2b::compiler_type::msvc; } + else if c_str == "clang" { stuff.active_compiler = cpp2b::compiler_type::clang; } + else if c_str == "gcc" { stuff.active_compiler = cpp2b::compiler_type::gcc; } + else { log_error("unknown compiler '{}'", c_str); std::exit(1); } + log_info("using compiler {}", c_str); + } cppfront_info_path: fs::path = ".cache/cpp2/tools/cppfront.info"; cppfront_bin: fs::path = ".cache/cpp2/tools/cppfront(executable_extension())$"; @@ -1155,7 +1160,7 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c repo.add("include"); } - exit_code = ensure_cppfront(repo.path()); + exit_code = ensure_cppfront(repo.path(), stuff.active_compiler); if exit_code != 0 { return; } if needs_recompile { @@ -1169,9 +1174,9 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c } } - ensure_std_modules(); - generate_cpp2b_module(); - generate_cpp2b_build_module(); + ensure_std_modules(stuff.active_compiler); + generate_cpp2b_module(stuff.active_compiler); + generate_cpp2b_build_module(stuff.active_compiler); transpile_source_dir: fs::path = ".cache/cpp2/source"; cpp2_source_files: std::vector = (); @@ -1267,7 +1272,7 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c build_script_futures.emplace_back( std::ref(build_script), - std::async(std::launch::async, build_build_script, build_script, stuff.cppfront_include_dir) + std::async(std::launch::async, build_build_script, build_script, stuff.cppfront_include_dir, stuff.active_compiler) ); } @@ -1373,7 +1378,7 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c std::exit(1); } - build_cpp1_module(cpp1m*, result.module_name, result.imports); + build_cpp1_module(cpp1m*, result.module_name, result.imports, stuff.active_compiler); built_modules[result.module_name] = true; } @@ -1414,7 +1419,7 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c bin_futures.emplace_back( std::ref(bin), - std::async(std::launch::async, build_binary, bin, stuff.cppfront_include_dir) + std::async(std::launch::async, build_binary, bin, stuff.cppfront_include_dir, stuff.active_compiler) ); } @@ -1782,6 +1787,13 @@ cpp2b_parse_source: (source_file: fs::path) -> cpp2b_source_info = { result.set_unknown(cpp2b_source_unknown_info(source_file)); if info.kind == cpp2b_build_info_parser::source_kind::build { + if std::ranges::find(info.imports, "cpp2b") == info.imports.end() { + info.imports.emplace_back("cpp2b"); + } + if std::ranges::find(info.imports, "cpp2b.build") == info.imports.end() { + info.imports.emplace_back("cpp2b.build"); + } + build_info: cpp2b_source_build_info = (); build_info.src = source_file; build_info.imports = info.imports; From b3dcb0e9e8af6c36d6b4b63dee8b47e738734bc3 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 8 May 2026 17:14:28 -0700 Subject: [PATCH 2/5] feat: remove crappy interpretter and just go with a basic enum --- build.cpp2 | 6 +- share/cpp2b/cpp2b.build.cppm.tpl | 11 +++- src/cpp2b_build_info_parser.cppm | 103 +------------------------------ src/main.cpp2 | 20 ++++-- 4 files changed, 30 insertions(+), 110 deletions(-) diff --git a/build.cpp2 b/build.cpp2 index 7ebf6a5..9d502b8 100644 --- a/build.cpp2 +++ b/build.cpp2 @@ -1,10 +1,8 @@ import cpp2b.build; cppfront :== "v0.8.1"; -compiler :== inspect cpp2b::host_platform() -> cpp2b::compiler_type { - is cpp2b::platform::windows = cpp2b::compiler_type::msvc; - is _ = cpp2b::compiler_type::clang; -}; +// compiler :== cpp2b::compiler_choice::preferred; +compiler :== cpp2b::compiler_choice::clang; build: (inout b: cpp2b::build) -> void = { _ = b.cpp1_module().source_path("src/dylib.cppm"); diff --git a/share/cpp2b/cpp2b.build.cppm.tpl b/share/cpp2b/cpp2b.build.cppm.tpl index 4c06d9a..42a912c 100644 --- a/share/cpp2b/cpp2b.build.cppm.tpl +++ b/share/cpp2b/cpp2b.build.cppm.tpl @@ -94,6 +94,14 @@ CPP2B_BUILD_DECL_FN( ); export namespace cpp2b { + +enum class compiler_choice { + preferred, + msvc, + gcc, + clang +}; + class git_repo { std::shared_ptr impl; @@ -148,7 +156,7 @@ public: std::source_location caller_srcloc = std::source_location::current() ) -> cpp1_module { CPP2B_BUILD_FN_CHECK(cpp2b_detail_cpp1_module_include_directory); - (*cpp2b_detail_cpp1_module_include_directory)(impl.get(), p, caller_srcloc); + (*cpp2b_detail_cpp1_module_include_directory)(impl.get(), p, caller_srcloc); return *this; } @@ -229,3 +237,4 @@ CPP2B_BUILD_API void cpp2b_detail_build(cpp2b_detail_build_impl* impl) { cpp2b::build b(impl); ::build(b); } + diff --git a/src/cpp2b_build_info_parser.cppm b/src/cpp2b_build_info_parser.cppm index efea8a4..429ed7a 100644 --- a/src/cpp2b_build_info_parser.cppm +++ b/src/cpp2b_build_info_parser.cppm @@ -94,8 +94,8 @@ source_info parse_source(const std::string& filename) { return true; }; - // Case 1: compiler :== cpp2b::compiler_type::; - if (match_path({"cpp2b", "compiler_type"})) { + // Case 1: compiler :== cpp2b::compiler_choice::; + if (match_path({"cpp2b", "compiler_choice"})) { bool has_scope = false; if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { k++; @@ -114,105 +114,6 @@ source_info parse_source(const std::string& filename) { continue; } } - // Case 2: compiler :== inspect cpp2b::host_platform() { ... } - else if (all_tokens[k].as_string_view() == "inspect") { - k++; - if (match_path({"cpp2b", "host_platform"})) { - if (k + 1 < all_tokens.size() && - all_tokens[k].as_string_view() == "(" && - all_tokens[k+1].as_string_view() == ")") { - - k += 2; - // Skip optional '-> result_type' - if (k < all_tokens.size() && - (all_tokens[k].as_string_view() == "->" || all_tokens[k].type() == cpp2::lexeme::Arrow)) { - k++; - while (k < all_tokens.size() && all_tokens[k].as_string_view() != "{") k++; - } - - if (k < all_tokens.size() && all_tokens[k].as_string_view() == "{") { - k += 1; - std::string_view host_platform = - - #if defined(_WIN32) - "windows"; - #elif defined(__APPLE__) - "macos"; - #else - "linux"; - #endif - - bool found = false; - while (k < all_tokens.size() && all_tokens[k].as_string_view() != "}") { - // is cpp2b::platform:: = cpp2b::compiler_type::; - // or is _ = ... - if (all_tokens[k].as_string_view() == "is") { - k++; - bool is_default = (all_tokens[k].as_string_view() == "_"); - std::string_view branch_platform = ""; - - if (!is_default) { - size_t saved_k_branch = k; - if (match_path({"cpp2b", "platform"})) { - bool has_scope = false; - if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { - k++; - has_scope = true; - } else if (k + 1 < all_tokens.size() && - all_tokens[k].as_string_view() == ":" && - all_tokens[k+1].as_string_view() == ":") { - k += 2; - has_scope = true; - } - - if (has_scope && k < all_tokens.size() && - (all_tokens[k].type() == cpp2::lexeme::Identifier || all_tokens[k].type() == cpp2::lexeme::Keyword)) { - branch_platform = all_tokens[k].as_string_view(); - k++; - } - } - } else { - k++; - } - - if (k < all_tokens.size() && all_tokens[k].as_string_view() == "=") { - k++; - if (match_path({"cpp2b", "compiler_type"})) { - bool has_scope = false; - if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { - k++; - has_scope = true; - } else if (k + 1 < all_tokens.size() && - all_tokens[k].as_string_view() == ":" && - all_tokens[k+1].as_string_view() == ":") { - k += 2; - has_scope = true; - } - - if (has_scope && k < all_tokens.size() && - (all_tokens[k].type() == cpp2::lexeme::Identifier || all_tokens[k].type() == cpp2::lexeme::Keyword)) { - - if (!found && (is_default || branch_platform == host_platform)) { - result.constants["compiler"] = std::string(all_tokens[k].as_string_view()); - found = true; - } - k++; - } - } - } - - } else { - k++; - } - } - if (found) { - j = k; - continue; - } - } - } - } - } } // identifier == string_literal ; diff --git a/src/main.cpp2 b/src/main.cpp2 index 907a5af..771fe45 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -1119,10 +1119,22 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c } if build_info.constants.contains("compiler") { c_str := build_info.constants.at("compiler"); - if c_str == "msvc" { stuff.active_compiler = cpp2b::compiler_type::msvc; } - else if c_str == "clang" { stuff.active_compiler = cpp2b::compiler_type::clang; } - else if c_str == "gcc" { stuff.active_compiler = cpp2b::compiler_type::gcc; } - else { log_error("unknown compiler '{}'", c_str); std::exit(1); } + if c_str == "msvc" { + stuff.active_compiler = cpp2b::compiler_type::msvc; + } else if c_str == "clang" { + stuff.active_compiler = cpp2b::compiler_type::clang; + } else if c_str == "gcc" { + stuff.active_compiler = cpp2b::compiler_type::gcc; + } else if c_str == "preferred" { + if cpp2b::host_platform() == cpp2b::platform::windows { + stuff.active_compiler = cpp2b::compiler_type::msvc; + } else { + stuff.active_compiler = cpp2b::compiler_type::clang; + } + } else { + log_error("unknown compiler '{}'", c_str); + std::exit(1); + } log_info("using compiler {}", c_str); } From 57e6b9d6d79d1da50a81ea8a4d2b673ff579d3b2 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 8 May 2026 17:57:25 -0700 Subject: [PATCH 3/5] feat: potentially windows clang compilation? --- share/cpp2b/cpp2b.cppm.tpl | 5 +- src/main.cpp2 | 231 +++++++++++++++++++++++++++---------- 2 files changed, 171 insertions(+), 65 deletions(-) diff --git a/share/cpp2b/cpp2b.cppm.tpl b/share/cpp2b/cpp2b.cppm.tpl index 4878587..4dae717 100644 --- a/share/cpp2b/cpp2b.cppm.tpl +++ b/share/cpp2b/cpp2b.cppm.tpl @@ -2,6 +2,7 @@ module; #ifdef _MSC_VER # include +# include #else # include # include @@ -12,9 +13,7 @@ export module cpp2b; import std; import std.compat; -#ifdef _MSC_VER -extern char** _environ; -#else +#if !defined(_MSC_VER) extern "C" char** environ; #endif diff --git a/src/main.cpp2 b/src/main.cpp2 index 771fe45..2b96099 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -96,6 +96,54 @@ expect: (move opt: std::optional, message) -> T = { return opt*; } +executable_extension: () -> std::string_view = { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + return ".exe"; + } else { + return ""; + } +} + +cmd_exists: (cmd: std::string) -> bool = { + check_cmd: std::string = ""; + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + check_cmd = std::format("where /q {}", cmd); + } else { + check_cmd = std::format("command -v {} > /dev/null 2>&1", cmd); + } + return std::system(check_cmd.c_str()) == 0; +} + +discover_clang_cmd: () -> std::string = { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + if cmd_exists("clang") { return "clang"; } + if cmd_exists("clang-cl") { return "clang-cl"; } + return "clang"; // fallback + } else { + if cmd_exists("clang-19") { return "clang-19"; } + return "clang"; + } +} + +discover_mingw_gxx: () -> std::string = { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + if cmd_exists("g++") { return "g++"; } + + // Common installation paths + common_paths: std::vector = ( + "C:/msys64/mingw64/bin/g++.exe", + "C:/msys64/ucrt64/bin/g++.exe", + "C:/msys64/clang64/bin/g++.exe", + "C:/MinGW/bin/g++.exe" + ); + + for common_paths do (p: fs::path) { + if fs::exists(p) { return p.string(); } + } + } + return "g++"; +} + GitRepo: type = { private repo_name: fs::path = (); private clone_url: std::string = (); @@ -160,16 +208,22 @@ Usage: module_source_extension: (compiler: cpp2b::compiler_type) -> std::string_view = { if compiler == cpp2b::compiler_type::msvc { return ".ixx"; } - if compiler == cpp2b::compiler_type::clang { return ".cppm"; } + if compiler == cpp2b::compiler_type::clang { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + return ".ixx"; + } else { + return ".cppm"; + } + } if compiler == cpp2b::compiler_type::gcc { return ".cxx"; } - std::exit(1); + return ".cppm"; } bmi_extension: (compiler: cpp2b::compiler_type) -> std::string_view = { if compiler == cpp2b::compiler_type::msvc { return ".ifc"; } if compiler == cpp2b::compiler_type::clang { return ".pcm"; } if compiler == cpp2b::compiler_type::gcc { return ".pcm"; } // TODO: Is this right? - std::exit(1); + return ".pcm"; } generate_cpp2b_module: (compiler: cpp2b::compiler_type) = { @@ -261,12 +315,32 @@ get_vs_tools_dir: () -> fs::path = { } get_llvm_root: () -> fs::path = { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + if cmd_exists("clang") { + // Typically C:\Program Files\LLVM\bin\clang.exe -> C:\Program Files\LLVM + // Let's check common paths first. + common_paths: std::vector = ( + "C:/Program Files/LLVM", + "C:/Program Files (x86)/LLVM" + ); + for common_paths do (p: fs::path) { + if fs::exists(p) { return p; } + } + } + return "C:/Program Files/LLVM"; + } return "/usr/lib/llvm-19"; } get_system_modules_dir: (compiler: cpp2b::compiler_type) -> fs::path = { if compiler == cpp2b::compiler_type::msvc { return get_vs_tools_dir() / "modules"; } - if compiler == cpp2b::compiler_type::clang { return get_llvm_root() / "share" / "libc++" / "v1"; } + if compiler == cpp2b::compiler_type::clang { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + return get_vs_tools_dir() / "modules"; + } else { + return get_llvm_root() / "share" / "libc++" / "v1"; + } + } log_error("cannot find system cpp20 modules directory"); std::exit(1); } @@ -293,9 +367,9 @@ ensure_std_modules: (compiler: cpp2b::compiler_type) = { } -cl_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, name: std::string, module_deps, bmi_path: fs::path) -> std::string = { +cl_build_cpp1_module_cmd: (compiler_cmd: std::string, impl: cpp2b_detail_cpp1_module_impl, name: std::string, module_deps, bmi_path: fs::path) -> std::string = { d := fs::absolute(modules_dir()); - cmd_str: std::string = "cl /nologo /std:c++latest /W4 /MDd /EHsc /c /interface /TP"; + cmd_str: std::string = std::format("{} /nologo /std:c++latest /W4 /MDd /EHsc /c /interface /TP", compiler_cmd); for impl.system_include_directories do (dir: fs::path) { cmd_str += " /I\"(fs::absolute(dir).string())$\""; @@ -317,9 +391,13 @@ cl_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, name: std::strin unix_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, compiler_cmd: std::string, name: std::string, module_deps, bmi_path: fs::path) -> std::string = { d := fs::absolute(modules_dir()); - sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; - cmd_str: std::string = std::format("{} -stdlib=libc++ -std=c++23 -fexperimental-library", compiler_cmd); - cmd_str += std::format(" -isystem \"{}\"", fs::absolute(sys_inc_dir).string()); + cmd_str: std::string = std::format("{} -std=c++23", compiler_cmd); + + if constexpr cpp2b::host_platform() != cpp2b::platform::windows { + cmd_str += " -stdlib=libc++ -fexperimental-library"; + sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; + cmd_str += std::format(" -isystem \"{}\"", fs::absolute(sys_inc_dir).string()); + } for impl.system_include_directories do (dir: fs::path) { cmd_str += std::format(" -isystem \"{}\"", fs::absolute(dir).string()); @@ -330,8 +408,10 @@ unix_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, compiler_cmd: } cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", fs::absolute(d).string()); - cmd_str += " \"(fs::absolute(impl.source_path).string())$\""; - cmd_str += std::format(" --precompile -o {}/{}.pcm", d.string(), name); + + // Clang uses .pcm for modules + cmd_str += std::format(" -x c++-module \"{}\"", fs::absolute(impl.source_path).string()); + cmd_str += std::format(" --precompile -o \"{}\"", fs::absolute(bmi_path).string()); return cmd_str; } @@ -344,10 +424,16 @@ build_cpp1_module: (impl: cpp2b_detail_cpp1_module_impl, name: std::string, modu ensure_dir(log_path.parent_path()); cmd_str: std::string = ""; - if compiler == cpp2b::compiler_type::msvc { cmd_str = cl_build_cpp1_module_cmd(impl, name, module_deps, bmi); } - else if compiler == cpp2b::compiler_type::clang { cmd_str = unix_build_cpp1_module_cmd(impl, "clang-19", name, module_deps, bmi); } - else if compiler == cpp2b::compiler_type::gcc { cmd_str = unix_build_cpp1_module_cmd(impl, "gcc", name, module_deps, bmi); } - else { log_error("unsupported compiler"); std::exit(1); } + if compiler == cpp2b::compiler_type::msvc { + cmd_str = cl_build_cpp1_module_cmd("cl", impl, name, module_deps, bmi); + } else if compiler == cpp2b::compiler_type::clang { + cmd_str = unix_build_cpp1_module_cmd(impl, discover_clang_cmd(), name, module_deps, bmi); + } else if compiler == cpp2b::compiler_type::gcc { + cmd_str = unix_build_cpp1_module_cmd(impl, discover_mingw_gxx(), name, module_deps, bmi); + } else { + log_error("unsupported compiler"); + std::exit(1); + } cmd_str += cmd_log_output(fs::absolute(log_path)); @@ -550,14 +636,6 @@ main: (args) -> int = { return 1; } -executable_extension: () -> std::string_view = { - if constexpr cpp2b::target_platform() == cpp2b::platform::windows { - return ".exe"; - } else { - return ""; - } -} - shared_library_extension: () -> std::string_view = { if constexpr cpp2b::target_platform() == cpp2b::platform::windows { return ".dll"; @@ -595,8 +673,9 @@ transpile_cpp2: (src: fs::path, out_dir: fs::path) -> transpile_cpp2_result = { result.output_log = fs::path(".cache") / "cpp2" / "log" / "transpile" / fs::path(src).replace_extension(".log"); ensure_dir(result.output_log.parent_path()); - cppfront := fs::absolute(".cache/cpp2/tools/cppfront(executable_extension())$"); - cmd_str: std::string = "(cppfront.string())$ (src.string())$ -o (result.cpp1_output.string())$ -pure -import-std -l -format-colon-errors (cmd_log_output(result.output_log))$"; + cppfront_bin_name: std::string = "cppfront(executable_extension())$"; + cppfront_exe := fs::absolute(".cache/cpp2/tools") / cppfront_bin_name; + cmd_str: std::string = std::format("{} {} -o {} -pure -import-std -l -format-colon-errors {}", cppfront_exe.string(), src.string(), result.cpp1_output.string(), cmd_log_output(result.output_log)); ensure_dir(result.cpp1_output.parent_path()); result.cppfront_exit_code = measure( @@ -616,9 +695,10 @@ unix_compile_cppfront: (cc: std::string_view, cppfront_source: fs::path, out_cpp return exit_code; } -cl_compile_cppfront: (cppfront_source: fs::path, out_cppfront_binary: fs::path, log_path: fs::path) -> int = { +cl_compile_cppfront: (compiler_cmd: std::string, cppfront_source: fs::path, out_cppfront_binary: fs::path, log_path: fs::path) -> int = { cwd := fs::current_path(); - cmd_str: std::string = "cl.exe /nologo /std:c++latest /EHsc cppfront.cpp (cmd_log_output(fs::absolute(log_path)))$"; + abs_log_path := fs::absolute(log_path); + cmd_str: std::string = std::format("{} /nologo /std:c++latest /EHsc cppfront.cpp {}", compiler_cmd, cmd_log_output(abs_log_path)); fs::current_path(cppfront_source / "source"); exit_code := std::system(cmd_str.c_str()); if exit_code == 0 { @@ -629,22 +709,31 @@ cl_compile_cppfront: (cppfront_source: fs::path, out_cppfront_binary: fs::path, } ensure_cppfront: (cppfront_source: fs::path, compiler: cpp2b::compiler_type) -> int = { - cppfront: fs::path = ".cache/cpp2/tools/cppfront(executable_extension())$"; - if !fs::exists(cppfront) { + cppfront_exe: fs::path = ".cache/cpp2/tools/cppfront(executable_extension())$"; + if !fs::exists(cppfront_exe) { log_info("compiling cppfront..."); cppfront_compile_log_path: fs::path = ".cache/cpp2/log/cppfront.log"; if compiler == cpp2b::compiler_type::clang { - return unix_compile_cppfront("clang-19", fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); + clang_cmd := discover_clang_cmd(); + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + if clang_cmd == "clang-cl" { + return cl_compile_cppfront(clang_cmd, fs::absolute(cppfront_source), fs::absolute(cppfront_exe), cppfront_compile_log_path); + } else { + return unix_compile_cppfront(clang_cmd, fs::absolute(cppfront_source), fs::absolute(cppfront_exe), cppfront_compile_log_path); + } + } else { + return unix_compile_cppfront(clang_cmd, fs::absolute(cppfront_source), fs::absolute(cppfront_exe), cppfront_compile_log_path); + } } if compiler == cpp2b::compiler_type::gcc { - return unix_compile_cppfront("gcc", fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); + return unix_compile_cppfront(discover_mingw_gxx(), fs::absolute(cppfront_source), fs::absolute(cppfront_exe), cppfront_compile_log_path); } if compiler == cpp2b::compiler_type::msvc { - return cl_compile_cppfront(fs::absolute(cppfront_source), fs::absolute(cppfront), cppfront_compile_log_path); + return cl_compile_cppfront("cl", fs::absolute(cppfront_source), fs::absolute(cppfront_exe), cppfront_compile_log_path); } log_error("unknown compiler"); @@ -702,14 +791,14 @@ build_binary_result: @struct type = { duration: std::chrono::milliseconds = (); } -cl_build_binary_cmd: (info: cpp2b_source_binary_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { +cl_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { transpiled_src := fs::absolute(".cache/cpp2/source") / fs::path(info.src).replace_extension(".cpp"); d := fs::absolute(modules_dir()); bin_parent_dir := fs::relative(bin_outpath).parent_path().string(); std::replace(bin_parent_dir.begin(), bin_parent_dir.end(), '/', '\\'); - cmd_str: std::string = "cl /nologo /std:c++latest /W4 /MDd /EHsc /DEBUG:full /Zi /FC"; + cmd_str: std::string = std::format("{} /nologo /std:c++latest /W4 /MDd /EHsc /DEBUG:full /Zi /FC", compiler_cmd); for info.imports do (imp: std::string) { imp_bmi := d / ("(imp)$.ifc"); imp_obj := d / ("(imp)$.obj"); @@ -724,20 +813,23 @@ cl_build_binary_cmd: (info: cpp2b_source_binary_info, bin_outpath: fs::path, cpp } unix_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { - sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; - sys_lib_dir := get_llvm_root() / "lib"; transpiled_src := fs::absolute(".cache/cpp2/source") / fs::path(info.src).replace_extension(".cpp"); d := fs::absolute(modules_dir()); - cmd_str: std::string = std::format("{} -stdlib=libc++ -fPIC", compiler_cmd); + cmd_str: std::string = std::format("{} -std=c++23", compiler_cmd); + + if constexpr cpp2b::host_platform() != cpp2b::platform::windows { + cmd_str += " -stdlib=libc++ -fPIC -fexperimental-library"; + sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; + sys_lib_dir := get_llvm_root() / "lib"; + cmd_str += std::format(" -L{} -isystem {}", sys_lib_dir.string(), sys_inc_dir.string()); + cmd_str += " -lc++abi -lc++ -lm -static -fuse-ld=lld"; + } + for info.imports do (imp: std::string) { - cmd_str += " \"" + (d / ("(imp)$.pcm")).string() + "\""; + cmd_str += std::format(" -fmodule-file={}=\"{}\"", imp, (d / ("(imp)$.pcm")).string()); } cmd_str += " \"(transpiled_src.string())$\""; - cmd_str += " -std=c++23 -fexperimental-library"; - cmd_str += std::format(" -fprebuilt-module-path={}", d.string()); - cmd_str += std::format(" -L{}", sys_lib_dir.string()); - cmd_str += std::format(" -isystem {}", sys_inc_dir.string()); - cmd_str += " -lc++abi -lc++ -lm -static -fuse-ld=lld"; + cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", d.string()); cmd_str += " -I\"(cppfront_include_dir.string())$\""; cmd_str += " -o \"(bin_outpath.string())$\""; return cmd_str; @@ -758,10 +850,16 @@ build_binary: (info: cpp2b_source_binary_info, cppfront_include_dir: fs::path, c d := fs::absolute(modules_dir()); cmd_str: std::string = ""; - if compiler == cpp2b::compiler_type::msvc { cmd_str = cl_build_binary_cmd(info, bin_outpath, cppfront_include_dir); } - else if compiler == cpp2b::compiler_type::clang { cmd_str = unix_build_binary_cmd("clang-19", info, bin_outpath, cppfront_include_dir); } - else if compiler == cpp2b::compiler_type::gcc { cmd_str = unix_build_binary_cmd("gcc", info, bin_outpath, cppfront_include_dir); } - else { log_error("Unsupported compiler"); std::exit(1); } + if compiler == cpp2b::compiler_type::msvc { + cmd_str = cl_build_binary_cmd("cl", info, bin_outpath, cppfront_include_dir); + } else if compiler == cpp2b::compiler_type::clang { + cmd_str = unix_build_binary_cmd(discover_clang_cmd(), info, bin_outpath, cppfront_include_dir); + } else if compiler == cpp2b::compiler_type::gcc { + cmd_str = unix_build_binary_cmd(discover_mingw_gxx(), info, bin_outpath, cppfront_include_dir); + } else { + log_error("Unsupported compiler"); + std::exit(1); + } cmd_str += " (cmd_log_output(fs::absolute(log_path)))$"; @@ -920,10 +1018,10 @@ cpp2b_detail_build: (copy _impl: *cpp2b_detail_build_impl) -> void = { // empty. this is just so we can decltype the signature } -cl_build_build_script_cmd: (info: cpp2b_source_build_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { +cl_build_build_script_cmd: (compiler_cmd: std::string, info: cpp2b_source_build_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { transpiled_src := fs::absolute(".cache/cpp2/source") / fs::path(info.src).replace_extension(".cpp"); d := fs::absolute(modules_dir()); - cmd_str: std::string = "cl /nologo /std:c++latest /W4 /MDd /EHsc /LDd /DLL"; + cmd_str: std::string = std::format("{} /nologo /std:c++latest /W4 /MDd /EHsc /LDd /DLL", compiler_cmd); for info.imports do (imp: std::string) { imp_bmi := d / ("(imp)$.ifc"); imp_obj := d / ("(imp)$.obj"); @@ -940,20 +1038,23 @@ cl_build_build_script_cmd: (info: cpp2b_source_build_info, bin_outpath: fs::path unix_build_build_script_cmd: (compiler_cmd: std::string, info: cpp2b_source_build_info, bin_outpath: fs::path, cppfront_include_dir: fs::path) -> std::string = { - sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; - sys_lib_dir := get_llvm_root() / "lib"; transpiled_src := fs::absolute(".cache/cpp2/source") / fs::path(info.src).replace_extension(".cpp"); d := fs::absolute(modules_dir()); - cmd_str: std::string = std::format("{} -stdlib=libc++ -shared", compiler_cmd); + cmd_str: std::string = std::format("{} -std=c++23 -shared", compiler_cmd); + + if constexpr cpp2b::host_platform() != cpp2b::platform::windows { + cmd_str += " -stdlib=libc++ -fexperimental-library -fPIC"; + sys_inc_dir := get_llvm_root() / "include" / "c++" / "v1"; + sys_lib_dir := get_llvm_root() / "lib"; + cmd_str += std::format(" -L{} -isystem {}", sys_lib_dir.string(), sys_inc_dir.string()); + cmd_str += " -lc++abi -lc++ -lm -fuse-ld=lld"; + } + for info.imports do (imp: std::string) { - cmd_str += " \"" + (d / ("(imp)$.pcm")).string() + "\""; + cmd_str += std::format(" -fmodule-file={}=\"{}\"", imp, (d / ("(imp)$.pcm")).string()); } cmd_str += " \"(transpiled_src.string())$\""; - cmd_str += " -std=c++23 -fexperimental-library -fPIC"; - cmd_str += std::format(" -fprebuilt-module-path={}", d.string()); - cmd_str += std::format(" -L{}", sys_lib_dir.string()); - cmd_str += std::format(" -isystem {}", sys_inc_dir.string()); - cmd_str += " -lc++abi -lc++ -lm -fuse-ld=lld"; + cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", d.string()); cmd_str += " -I\"(cppfront_include_dir.string())$\""; cmd_str += " -o \"(bin_outpath.string())$\""; return cmd_str; @@ -968,10 +1069,16 @@ build_build_script: (info: cpp2b_source_build_info, cppfront_include_dir: fs::pa d := fs::absolute(modules_dir()); cmd_str: std::string = ""; - if compiler == cpp2b::compiler_type::msvc { cmd_str = cl_build_build_script_cmd(info, bin_outpath, cppfront_include_dir); } - else if compiler == cpp2b::compiler_type::clang { cmd_str = unix_build_build_script_cmd("clang-19", info, bin_outpath, cppfront_include_dir); } - else if compiler == cpp2b::compiler_type::gcc { cmd_str = unix_build_build_script_cmd("gcc", info, bin_outpath, cppfront_include_dir); } - else { log_error("Unsupported compiler"); std::exit(1); } + if compiler == cpp2b::compiler_type::msvc { + cmd_str = cl_build_build_script_cmd("cl", info, bin_outpath, cppfront_include_dir); + } else if compiler == cpp2b::compiler_type::clang { + cmd_str = unix_build_build_script_cmd(discover_clang_cmd(), info, bin_outpath, cppfront_include_dir); + } else if compiler == cpp2b::compiler_type::gcc { + cmd_str = unix_build_build_script_cmd(discover_mingw_gxx(), info, bin_outpath, cppfront_include_dir); + } else { + log_error("Unsupported compiler"); + std::exit(1); + } cmd_str += " (cmd_log_output(fs::relative(log_path)))$"; From 84e37f5a38728ab6f71a8833ddae59e1518e6f50 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 8 May 2026 19:20:19 -0700 Subject: [PATCH 4/5] feat: clang-ish support on windows --- build.cpp2 | 4 ++-- src/main.cpp2 | 45 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/build.cpp2 b/build.cpp2 index 9d502b8..c096884 100644 --- a/build.cpp2 +++ b/build.cpp2 @@ -1,8 +1,8 @@ import cpp2b.build; cppfront :== "v0.8.1"; -// compiler :== cpp2b::compiler_choice::preferred; -compiler :== cpp2b::compiler_choice::clang; +compiler :== cpp2b::compiler_choice::preferred; +// compiler :== cpp2b::compiler_choice::clang; build: (inout b: cpp2b::build) -> void = { _ = b.cpp1_module().source_path("src/dylib.cppm"); diff --git a/src/main.cpp2 b/src/main.cpp2 index 2b96099..29c1178 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -104,6 +104,14 @@ executable_extension: () -> std::string_view = { } } +obj_extension: () -> std::string_view = { + if constexpr cpp2b::host_platform() == cpp2b::platform::windows { + return ".obj"; + } else { + return ".o"; + } +} + cmd_exists: (cmd: std::string) -> bool = { check_cmd: std::string = ""; if constexpr cpp2b::host_platform() == cpp2b::platform::windows { @@ -409,9 +417,22 @@ unix_build_cpp1_module_cmd: (impl: cpp2b_detail_cpp1_module_impl, compiler_cmd: cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", fs::absolute(d).string()); - // Clang uses .pcm for modules - cmd_str += std::format(" -x c++-module \"{}\"", fs::absolute(impl.source_path).string()); - cmd_str += std::format(" --precompile -o \"{}\"", fs::absolute(bmi_path).string()); + // Clang uses .pcm for modules. If it's clang, also generate the object file. + if compiler_cmd.find("clang") != std::string::npos { + obj_path := bmi_path; + obj_path.replace_extension(obj_extension()); + cmd_str += std::format(" -x c++-module \"{}\" -c -o \"{}\" -fmodule-output=\"{}\"", + fs::absolute(impl.source_path).string(), + fs::absolute(obj_path).string(), + fs::absolute(bmi_path).string() + ); + } else { + cmd_str += std::format(" -x c++-module \"{}\" --precompile -o \"{}\"", + fs::absolute(impl.source_path).string(), + fs::absolute(bmi_path).string() + ); + } + return cmd_str; } @@ -688,7 +709,11 @@ transpile_cpp2: (src: fs::path, out_dir: fs::path) -> transpile_cpp2_result = { unix_compile_cppfront: (cc: std::string_view, cppfront_source: fs::path, out_cppfront_binary: fs::path, log_path: fs::path) -> int = { cwd := fs::current_path(); - cmd_str: std::string = "(cc)$ -lstdc++ -lc -lm -std=c++20 cppfront.cpp -o (out_cppfront_binary.string())$ (cmd_log_output(fs::absolute(log_path)))$"; + cmd_str: std::string = std::format("{} -std=c++20 cppfront.cpp -o {}", cc, out_cppfront_binary.string()); + if constexpr cpp2b::host_platform() != cpp2b::platform::windows { + cmd_str += " -lstdc++ -lc -lm"; + } + cmd_str += cmd_log_output(fs::absolute(log_path)); fs::current_path(cppfront_source / "source"); exit_code := std::system(cmd_str.c_str()); fs::current_path(cwd); @@ -827,6 +852,12 @@ unix_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_inf for info.imports do (imp: std::string) { cmd_str += std::format(" -fmodule-file={}=\"{}\"", imp, (d / ("(imp)$.pcm")).string()); + + // Link the object file if it exists + obj_path := d / (imp + std::string(obj_extension())); + if fs::exists(obj_path) { + cmd_str += " \"" + obj_path.string() + "\""; + } } cmd_str += " \"(transpiled_src.string())$\""; cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", d.string()); @@ -1052,6 +1083,12 @@ unix_build_build_script_cmd: (compiler_cmd: std::string, info: cpp2b_source_buil for info.imports do (imp: std::string) { cmd_str += std::format(" -fmodule-file={}=\"{}\"", imp, (d / ("(imp)$.pcm")).string()); + + // Link the object file if it exists + obj_path := d / (imp + std::string(obj_extension())); + if fs::exists(obj_path) { + cmd_str += " \"" + obj_path.string() + "\""; + } } cmd_str += " \"(transpiled_src.string())$\""; cmd_str += std::format(" -fprebuilt-module-path=\"{}\"", d.string()); From bb7eab099a7ddc3291bbb66855433db28892c8a4 Mon Sep 17 00:00:00 2001 From: Ezekiel Warren Date: Fri, 8 May 2026 19:24:14 -0700 Subject: [PATCH 5/5] chore: formatting --- build.cpp2 | 1 - src/cpp2b_build_info_parser.cppm | 104 ++++++++++++++++--------------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/build.cpp2 b/build.cpp2 index c096884..58ed6dc 100644 --- a/build.cpp2 +++ b/build.cpp2 @@ -2,7 +2,6 @@ import cpp2b.build; cppfront :== "v0.8.1"; compiler :== cpp2b::compiler_choice::preferred; -// compiler :== cpp2b::compiler_choice::clang; build: (inout b: cpp2b::build) -> void = { _ = b.cpp1_module().source_path("src/dylib.cppm"); diff --git a/src/cpp2b_build_info_parser.cppm b/src/cpp2b_build_info_parser.cppm index 429ed7a..d704ea8 100644 --- a/src/cpp2b_build_info_parser.cppm +++ b/src/cpp2b_build_info_parser.cppm @@ -66,50 +66,56 @@ source_info parse_source(const std::string& filename) { auto name = all_tokens[j].as_string_view(); // Specific handling for 'compiler :== ...' - if (name == "compiler" && j + 2 < all_tokens.size() && - all_tokens[j+1].type() == cpp2::lexeme::Colon && - all_tokens[j+2].type() == cpp2::lexeme::EqualComparison) { - + if(name == "compiler" && j + 2 < all_tokens.size() && + all_tokens[j + 1].type() == cpp2::lexeme::Colon && + all_tokens[j + 2].type() == cpp2::lexeme::EqualComparison) { size_t k = j + 3; - auto match_path = [&](const std::vector& parts) { - size_t cur = k; - for (size_t i = 0; i < parts.size(); ++i) { - if (cur >= all_tokens.size()) return false; - if (all_tokens[cur].as_string_view() != parts[i]) return false; - cur++; - if (i < parts.size() - 1) { - if (cur >= all_tokens.size()) return false; - if (all_tokens[cur].as_string_view() == "::") { - cur++; - } else if (cur + 1 < all_tokens.size() && - all_tokens[cur].as_string_view() == ":" && - all_tokens[cur+1].as_string_view() == ":") { - cur += 2; - } else { + auto match_path = [&](const std::vector& parts) { + size_t cur = k; + for(size_t i = 0; i < parts.size(); ++i) { + if(cur >= all_tokens.size()) { + return false; + } + if(all_tokens[cur].as_string_view() != parts[i]) { return false; } + cur++; + if(i < parts.size() - 1) { + if(cur >= all_tokens.size()) { + return false; + } + if(all_tokens[cur].as_string_view() == "::") { + cur++; + } else if(cur + 1 < all_tokens.size() && + all_tokens[cur].as_string_view() == ":" && + all_tokens[cur + 1].as_string_view() == ":") { + cur += 2; + } else { + return false; + } + } } - } - k = cur; - return true; - }; + k = cur; + return true; + }; // Case 1: compiler :== cpp2b::compiler_choice::; - if (match_path({"cpp2b", "compiler_choice"})) { + if(match_path({"cpp2b", "compiler_choice"})) { bool has_scope = false; - if (k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { + if(k < all_tokens.size() && all_tokens[k].as_string_view() == "::") { k++; has_scope = true; - } else if (k + 1 < all_tokens.size() && - all_tokens[k].as_string_view() == ":" && - all_tokens[k+1].as_string_view() == ":") { + } else if(k + 1 < all_tokens.size() && + all_tokens[k].as_string_view() == ":" && + all_tokens[k + 1].as_string_view() == ":") { k += 2; has_scope = true; } - if (has_scope && k < all_tokens.size() && - all_tokens[k].type() == cpp2::lexeme::Identifier) { - result.constants["compiler"] = std::string(all_tokens[k].as_string_view()); + if(has_scope && k < all_tokens.size() && + all_tokens[k].type() == cpp2::lexeme::Identifier) { + result.constants["compiler"] = + std::string(all_tokens[k].as_string_view()); j = k; continue; } @@ -131,26 +137,26 @@ source_info parse_source(const std::string& filename) { j += 3; } // identifier :== string_literal ; - else if (j + 3 < all_tokens.size() && - all_tokens[j].type() == cpp2::lexeme::Identifier && - all_tokens[j+1].type() == cpp2::lexeme::Colon && - all_tokens[j+2].type() == cpp2::lexeme::EqualComparison && - all_tokens[j+3].type() == cpp2::lexeme::StringLiteral) { - auto name = all_tokens[j].as_string_view(); - auto value_raw = all_tokens[j+3].as_string_view(); - if (value_raw.size() >= 2) { - auto value = std::string(value_raw.substr(1, value_raw.size() - 2)); - result.constants[std::string(name)] = value; - } - j += 3; + else if(j + 3 < all_tokens.size() && + all_tokens[j].type() == cpp2::lexeme::Identifier && + all_tokens[j + 1].type() == cpp2::lexeme::Colon && + all_tokens[j + 2].type() == cpp2::lexeme::EqualComparison && + all_tokens[j + 3].type() == cpp2::lexeme::StringLiteral) { + auto name = all_tokens[j].as_string_view(); + auto value_raw = all_tokens[j + 3].as_string_view(); + if(value_raw.size() >= 2) { + auto value = std::string(value_raw.substr(1, value_raw.size() - 2)); + result.constants[std::string(name)] = value; + } + j += 3; } // identifier : type == string_literal ; - else if (j + 5 < all_tokens.size() && - all_tokens[j].type() == cpp2::lexeme::Identifier && - all_tokens[j+1].type() == cpp2::lexeme::Colon && - all_tokens[j+3].type() == cpp2::lexeme::EqualComparison && - all_tokens[j+4].type() == cpp2::lexeme::StringLiteral && - all_tokens[j+5].type() == cpp2::lexeme::Semicolon) { + else if(j + 5 < all_tokens.size() && + all_tokens[j].type() == cpp2::lexeme::Identifier && + all_tokens[j + 1].type() == cpp2::lexeme::Colon && + all_tokens[j + 3].type() == cpp2::lexeme::EqualComparison && + all_tokens[j + 4].type() == cpp2::lexeme::StringLiteral && + all_tokens[j + 5].type() == cpp2::lexeme::Semicolon) { auto name = all_tokens[j].as_string_view(); auto value_raw = all_tokens[j + 4].as_string_view(); if(value_raw.size() >= 2) {