diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index bef6d66..04439a6 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -18,6 +18,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + submodules: recursive - uses: crate-ci/typos@cfe759ac8dd421e203cc293a373396fbc6fe0d4b # v1.22.7 build-script-windows: @@ -25,6 +27,8 @@ jobs: runs-on: windows-2025-vs2026 steps: - uses: actions/checkout@v4 + with: + submodules: recursive - run: .\build.cmd - uses: actions/upload-artifact@v4 with: @@ -39,6 +43,8 @@ jobs: runs-on: windows-2025-vs2026 steps: - uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/download-artifact@v4 with: name: cpp2b-windows-x86_64 @@ -56,6 +62,8 @@ jobs: - run: rm ./llvm.sh - run: sudo apt-get install libc++-19-dev ninja-build libclang-19-dev -y - uses: actions/checkout@v4 + with: + submodules: recursive - run: ./build.sh - uses: actions/upload-artifact@v4 with: @@ -75,6 +83,8 @@ jobs: - run: rm ./llvm.sh - run: sudo apt-get install libc++-19-dev ninja-build libclang-19-dev -y - uses: actions/checkout@v4 + with: + submodules: recursive - uses: actions/download-artifact@v4 with: name: cpp2b-linux-x86_64 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9f615c0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cppfront"] + path = cppfront + url = https://github.com/hsutter/cppfront.git diff --git a/build.cmd b/build.cmd index c4339e4..39965a2 100644 --- a/build.cmd +++ b/build.cmd @@ -4,7 +4,7 @@ setlocal enabledelayedexpansion set root_dir=%~dp0 set tools_dir=%~dp0.cache\tools\ set cppfront=%tools_dir%\cppfront.exe -set cppfront_include_dir=%~dp0.cache\repos\cppfront\include +set cppfront_include_dir=%~dp0cppfront\source set cpp2b_dist=%~dp0dist\debug\cpp2b set modules_dir=%~dp0.cache\modules @@ -140,8 +140,23 @@ if %ERRORLEVEL% neq 0 ( exit %ERRORLEVEL% ) +echo INFO: compiling cpp2b_build_info_parser module... +pushd %modules_dir% +cl /nologo ^ + /std:c++latest /W4 /MDd /EHsc ^ + -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 +popd + +if %ERRORLEVEL% neq 0 ( + echo ERROR: failed to compile cpp2b_build_info_parser module + exit %ERRORLEVEL% +) + if not exist %cppfront% ( - pushd .cache\repos\cppfront\source + pushd cppfront\source echo INFO: compiling cppfront... cl /nologo /std:c++latest /EHsc cppfront.cpp xcopy cppfront.exe %tools_dir% /Y /Q @@ -167,6 +182,7 @@ cl /nologo "%root_dir%.cache/cpp2/source/src/main.cpp" ^ /reference "%modules_dir%\dylib.ifc" "%modules_dir%\dylib.obj" ^ /reference "%modules_dir%\nlohmann.json.ifc" "%modules_dir%\nlohmann.json.obj" ^ /reference "%modules_dir%\cpp2b.ifc" "%modules_dir%\cpp2b.obj" ^ + /reference "%modules_dir%\cpp2b_build_info_parser.ifc" "%modules_dir%\cpp2b_build_info_parser.obj" ^ /std:c++latest /W4 /MDd /EHsc ^ /DEBUG:FULL /Zi /FC ^ -I"%cppfront_include_dir%" ^ diff --git a/build.cpp2 b/build.cpp2 index f02c758..d2baad7 100644 --- a/build.cpp2 +++ b/build.cpp2 @@ -1,7 +1,12 @@ import cpp2b.build; +cppfront :== "v0.8.1"; + build: (inout b: cpp2b::build) -> void = { _ = b.cpp1_module().source_path("src/dylib.cppm"); _ = b.cpp1_module().source_path("src/nlohmann.json.cppm"); + _ = b.cpp1_module() + .source_path("src/cpp2b_build_info_parser.cppm") + .include_directory("cppfront/source"); b.binary_name("src/main", "cpp2b"); } diff --git a/build.sh b/build.sh index a70f80f..2fd9b20 100755 --- a/build.sh +++ b/build.sh @@ -60,40 +60,13 @@ fi log_info "using compiler '$CPP2B_COMPILER' version '$COMPILER_VERSION'" -function ensure_gh_repo() { - local repo=$1 - local branch=$2 - local repo_path=$ROOT_DIR/.cache/repos/$repo - if ! [ -d $repo_path ]; then - mkdir -p $repo_path - git clone --quiet --depth=1 --branch=$branch --filter=blob:none --sparse https://github.com/$repo $repo_path - fi -} - -function ensure_gh_repo_subdir() { - local repo=$1 - local repo_path=$ROOT_DIR/.cache/repos/$repo - local repo_subdir=$2 - local repo_subdir_path=$repo_path/$repo_subdir - if ! [ -d $repo_subdir_path ]; then - cd $repo_path - log_info "checking out repo $repo/$reposubdir" - git sparse-checkout add $repo_subdir - cd $ROOT_DIR - fi -} - -ensure_gh_repo "hsutter/cppfront" "v0.8.1" -ensure_gh_repo_subdir "hsutter/cppfront" "source" -ensure_gh_repo_subdir "hsutter/cppfront" "include" - -CPPFRONT_INCLUDE_DIR=$ROOT_DIR/.cache/repos/hsutter/cppfront/include +CPPFRONT_INCLUDE_DIR=$ROOT_DIR/cppfront/source LLVM_ROOT=/usr/lib/llvm-$COMPILER_MAJOR_VERSION if ! [ -x $CPPFRONT ]; then log_info "compiling cppfront..." - cd $ROOT_DIR/.cache/repos/hsutter/cppfront/source + cd $ROOT_DIR/cppfront/source $CPP2B_COMPILER \ -std=c++23 \ -stdlib=libc++ \ @@ -176,6 +149,20 @@ if ! [ -f $MODULES_DIR/nlohmann.json.pcm ]; then cd $ROOT_DIR fi +if ! [ -f $MODULES_DIR/cpp2b_build_info_parser.pcm ]; then + log_info "compiling cpp2b_build_info_parser module..." + + $CPP2B_COMPILER \ + -stdlib=libc++ \ + -std=c++23 \ + -fexperimental-library \ + -isystem $LLVM_ROOT/include/c++/v1 \ + -fprebuilt-module-path=$MODULES_DIR \ + -I"$CPPFRONT_INCLUDE_DIR" \ + "$ROOT_DIR/src/cpp2b_build_info_parser.cppm" \ + --precompile -o $MODULES_DIR/cpp2b_build_info_parser.pcm +fi + log_info "compiling cpp2b module..." if [ -f "$ROOT_DIR/.cache/cpp2/source/_build/cpp2b.cppm" ]; then rm "$ROOT_DIR/.cache/cpp2/source/_build/cpp2b.cppm" @@ -199,10 +186,16 @@ log_info "compiling..." $CPP2B_COMPILER \ -g \ -stdlib=libc++ \ + -fmodule-file=cpp2b="$MODULES_DIR/cpp2b.pcm" \ + -fmodule-file=dylib="$MODULES_DIR/dylib.pcm" \ + -fmodule-file=std.compat="$MODULES_DIR/std.compat.pcm" \ + -fmodule-file=nlohmann.json="$MODULES_DIR/nlohmann.json.pcm" \ + -fmodule-file=cpp2b_build_info_parser="$MODULES_DIR/cpp2b_build_info_parser.pcm" \ "$MODULES_DIR/cpp2b.pcm" \ "$MODULES_DIR/dylib.pcm" \ "$MODULES_DIR/std.compat.pcm" \ "$MODULES_DIR/nlohmann.json.pcm" \ + "$MODULES_DIR/cpp2b_build_info_parser.pcm" \ "$ROOT_DIR/.cache/cpp2/source/src/main.cpp" \ -std=c++23 \ -fexperimental-library \ diff --git a/cppfront b/cppfront new file mode 160000 index 0000000..ec1f98a --- /dev/null +++ b/cppfront @@ -0,0 +1 @@ +Subproject commit ec1f98abd3752459e372f0228041e233f0fd6c7e diff --git a/src/cpp2b_build_info_parser.cppm b/src/cpp2b_build_info_parser.cppm new file mode 100644 index 0000000..f793d9c --- /dev/null +++ b/src/cpp2b_build_info_parser.cppm @@ -0,0 +1,135 @@ +module; +#include "parse.h" +export module cpp2b_build_info_parser; + +import std; + +export namespace cpp2b_build_info_parser { + +enum class source_kind { unknown, module, binary, build }; + +struct source_info { + source_kind kind = source_kind::unknown; + std::string module_name; + std::vector imports; + std::map constants; + bool exported = false; +}; + +source_info parse_source(const std::string& filename) { + auto result = source_info{}; + auto errors = std::vector{}; + auto source = cpp2::source{errors}; + + if(!source.load(filename)) { + return result; + } + + // Extract imports from source lines + for(auto const& line : source.get_lines()) { + if(line.cat == cpp2::source_line::category::import) { + std::string text = line.text; + // Basic extraction: import ; + size_t import_pos = text.find("import"); + if(import_pos != std::string::npos) { + std::string import_name; + size_t k = import_pos + 6; + while(k < text.size() && std::isspace(text[k])) { + k++; + } + while(k < text.size() && !std::isspace(text[k]) && text[k] != ';') { + import_name += text[k]; + k++; + } + if(!import_name.empty()) { + result.imports.emplace_back(import_name); + } + } + } + } + + cpp2::tokens tokens(errors); + tokens.lex(source.get_lines()); + + std::vector all_tokens; + for(auto const& [line, line_tokens] : tokens.get_map()) { + for(auto const& t : line_tokens) { + all_tokens.push_back(t); + } + } + + std::filesystem::path p(filename); + if(p.filename() == "build.cpp2") { + result.kind = source_kind::build; + // Extract constants + for(size_t j = 0; j < all_tokens.size(); ++j) { + // identifier == string_literal ; + if(j + 3 < all_tokens.size() && + all_tokens[j].type() == cpp2::lexeme::Identifier && + all_tokens[j + 1].type() == cpp2::lexeme::EqualComparison && + all_tokens[j + 2].type() == cpp2::lexeme::StringLiteral && + all_tokens[j + 3].type() == cpp2::lexeme::Semicolon) { + auto name = all_tokens[j].as_string_view(); + auto value_raw = all_tokens[j + 2].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 :== 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; + } + // 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) { + auto name = all_tokens[j].as_string_view(); + auto value_raw = all_tokens[j + 4].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 += 5; + } + } + } else { + // Module detection + for(size_t k = 0; k + 2 < all_tokens.size(); ++k) { + if(all_tokens[k].as_string_view() == "export" && + all_tokens[k + 1].as_string_view() == "module" && + all_tokens[k + 2].type() == cpp2::lexeme::Identifier) { + result.kind = source_kind::module; + result.module_name = std::string(all_tokens[k + 2].as_string_view()); + result.exported = true; + return result; + } + } + + // Binary detection + for(auto const& t : all_tokens) { + if(t.type() == cpp2::lexeme::Identifier && t.as_string_view() == "main") { + result.kind = source_kind::binary; + return result; + } + } + } + + return result; +} + +} // namespace cpp2b_build_info_parser diff --git a/src/main.cpp2 b/src/main.cpp2 index 0f8b718..3abdfeb 100644 --- a/src/main.cpp2 +++ b/src/main.cpp2 @@ -2,6 +2,7 @@ import std.compat; import cpp2b; import dylib; import nlohmann.json; +import cpp2b_build_info_parser; fs: namespace == std::filesystem; json: type == nlohmann::json; @@ -95,15 +96,37 @@ expect: (move opt: std::optional, message) -> T = { return opt*; } -GitHubRepo: type = { - private repo_: fs::path; +GitRepo: type = { + private repo_name: fs::path = (); + private clone_url: std::string = (); + private branch: std::string = (); - operator=: (out this, repo, branch) = { - repo_ = repo; + operator=: (out this, repo: std::string_view, repo_branch: std::string_view) = { + branch = std::string(repo_branch); + clone_url = std::string(repo); + m: git_clone_url_matcher = (); + result := m.regex.match(clone_url); + + if result.matched { + host := result.group(1); + owner := result.group(2); + name := result.group(3); + repo_name = fs::path(host) / owner / name; + } else { + log_error("invalid git clone url: {}", clone_url); + log_error("expected format: [proto://]host/owner/repo[.git]"); + std::exit(1); + } + } + + fetch: (this) = { if !fs::exists(path() / ".git") { ensure_dir(path()); - git_("clone --depth=1 --branch=(branch)$ --filter=blob:none --sparse https://github.com/(repo_.generic_string())$ ."); + git_("clone --depth=1 --branch=(branch)$ --filter=blob:none --sparse (clone_url)$ ."); + } else { + git_("fetch origin (branch)$ --depth=1"); + git_("checkout FETCH_HEAD"); } } @@ -116,7 +139,7 @@ GitHubRepo: type = { } path: (this) -> fs::path = { - return fs::current_path() / ".cache" / "cpp2" / "repos" / repo_; + return fs::current_path() / ".cache" / "cpp2" / "repos" / repo_name; } add: (this, subpath: std::string) = { @@ -682,8 +705,7 @@ build_binary_result: @struct type = { duration: std::chrono::milliseconds = (); } -cl_build_binary_cmd: (info: cpp2b_source_binary_info, bin_outpath: fs::path) -> std::string = { - cppfront_include_dir := fs::absolute(".cache/cpp2/repos/hsutter/cppfront/include"); +cl_build_binary_cmd: (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()); @@ -704,10 +726,9 @@ cl_build_binary_cmd: (info: cpp2b_source_binary_info, bin_outpath: fs::path) -> return cmd_str; } -unix_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_info, bin_outpath: fs::path) -> std::string = { +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"; - cppfront_include_dir := fs::absolute(".cache/cpp2/repos/hsutter/cppfront/include"); 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); @@ -726,7 +747,7 @@ unix_build_binary_cmd: (compiler_cmd: std::string, info: cpp2b_source_binary_inf } -build_binary: (info: cpp2b_source_binary_info) -> build_binary_result = { +build_binary: (info: cpp2b_source_binary_info, cppfront_include_dir: fs::path) -> build_binary_result = { compiler :== cpp2b::compiler(); bin_basename: fs::path = info.name(); if bin_basename.extension().empty() { @@ -741,9 +762,9 @@ build_binary: (info: cpp2b_source_binary_info) -> build_binary_result = { d := fs::absolute(modules_dir()); cmd_str: std::string = ""; - if compiler == cpp2b::compiler_type::msvc { cmd_str = cl_build_binary_cmd(info, bin_outpath); } - else if compiler == cpp2b::compiler_type::clang { cmd_str = unix_build_binary_cmd("clang-19", info, bin_outpath); } - else if compiler == cpp2b::compiler_type::gcc { cmd_str = unix_build_binary_cmd("gcc", info, bin_outpath); } + 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); } cmd_str += " (cmd_log_output(fs::absolute(log_path)))$"; @@ -873,10 +894,16 @@ cpp2b_detail_git_clone: ( m: git_clone_url_matcher = (); result := m.regex.match(repo_impl*.clone_url); - - repo_impl*.clone_host = result.group(1); - repo_impl*.clone_owner_dirname = result.group(2); - repo_impl*.clone_repo_dirname = result.group(3); + + if result.matched { + repo_impl*.clone_host = result.group(1); + repo_impl*.clone_owner_dirname = result.group(2); + repo_impl*.clone_repo_dirname = result.group(3); + } else { + log_error("invalid git clone url: {}", repo_impl*.clone_url); + log_error("expected format: [proto://]host/owner/repo[.git]"); + std::exit(1); + } impl*.git_repos.emplace_back(repo_impl); @@ -897,15 +924,15 @@ 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) -> std::string = { - cppfront_include_dir := fs::absolute(".cache/cpp2/repos/hsutter/cppfront/include"); +cl_build_build_script_cmd: (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"; for info.imports do (imp: std::string) { imp_bmi := d / ("(imp)$.ifc"); imp_obj := d / ("(imp)$.obj"); - cmd_str += " /reference \"(imp_bmi.string())$\" \"(imp_obj.string())$\""; + cmd_str += " /reference \"(imp_bmi.string())$\""; + cmd_str += " \"(imp_obj.string())$\""; } cmd_str += " \"(transpiled_src .string())$\""; cmd_str += " -I\"(cppfront_include_dir.string())$\""; @@ -916,10 +943,9 @@ 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) -> std::string = { +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"; - cppfront_include_dir := fs::absolute(".cache/cpp2/repos/hsutter/cppfront/include"); 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); @@ -938,7 +964,7 @@ unix_build_build_script_cmd: (compiler_cmd: std::string, info: cpp2b_source_buil } -build_build_script: (info: cpp2b_source_build_info) -> build_binary_result = { +build_build_script: (info: cpp2b_source_build_info, cppfront_include_dir: fs::path) -> build_binary_result = { compiler :== cpp2b::compiler(); 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"); @@ -947,9 +973,9 @@ build_build_script: (info: cpp2b_source_build_info) -> build_binary_result = { 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); } - else if compiler == cpp2b::compiler_type::clang { cmd_str = unix_build_build_script_cmd("clang-19", info, bin_outpath); } - else if compiler == cpp2b::compiler_type::gcc { cmd_str = unix_build_build_script_cmd("gcc", info, bin_outpath); } + 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); } cmd_str += " (cmd_log_output(fs::relative(log_path)))$"; @@ -982,6 +1008,7 @@ full_build_info: @struct type = { mods: std::vector = (); build_scripts: std::vector = (); unknowns: std::vector = (); + cppfront_include_dir: fs::path = (); bin_results: std::vector = (); @@ -1079,11 +1106,67 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c build_cpp2_dir := fs::current_path(); stuff = (); - (repo := GitHubRepo("hsutter/cppfront", "v0.8.1")) { - repo.add("source"); - repo.add("include"); + cppfront_url: std::string = "https://github.com/hsutter/cppfront.git"; + cppfront: std::string = "v0.8.1"; + + expect_file("build.cpp2"); + build_info_variant := cpp2b_parse_source("build.cpp2"); + 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"); + } + + cppfront_info_path: fs::path = ".cache/cpp2/tools/cppfront.info"; + cppfront_bin: fs::path = ".cache/cpp2/tools/cppfront(executable_extension())$"; + needs_recompile: bool = false; + if fs::exists(cppfront_info_path) && fs::exists(cppfront_bin) { + info_stream: std::ifstream = cppfront_info_path; + stored_url: std::string = (); + stored_cppfront: std::string = (); + std::getline(info_stream, stored_url); + std::getline(info_stream, stored_cppfront); + if stored_url != cppfront_url { + log_info("cppfront_url changed: {} -> {}", stored_url, cppfront_url); + needs_recompile = true; + } + if stored_cppfront != cppfront { + log_info("cppfront changed: {} -> {}", stored_cppfront, cppfront); + needs_recompile = true; + } + } else { + needs_recompile = true; + } + + if needs_recompile { + if fs::exists(cppfront_bin) { + fs::remove(cppfront_bin); + } + } + + (repo := GitRepo(cppfront_url, cppfront)) { + if needs_recompile || !fs::exists(repo.path() / ".git") { + repo.fetch(); + repo.add("source"); + repo.add("include"); + } + exit_code = ensure_cppfront(repo.path()); if exit_code != 0 { return; } + + if needs_recompile { + info_out: std::ofstream = (cppfront_info_path, std::ios::binary); + info_out << cppfront_url << "\n" << cppfront << "\n"; + } + + stuff.cppfront_include_dir = repo.path() / "source"; + if !fs::exists(stuff.cppfront_include_dir) { + stuff.cppfront_include_dir = repo.path() / "include"; + } } ensure_std_modules(); @@ -1106,7 +1189,8 @@ do_build: (targets: std::vector) -> (stuff: full_build_info, exit_c if p.extension() == ".cpp2" { rel_path := fs::relative(p, fs::current_path()); for rel_path do(rel_path_comp) { - if rel_path_comp.string().starts_with(".") { + comp := rel_path_comp.string(); + if comp.starts_with(".") || comp == "cppfront" || comp == "dist" { continue src_loop; } } @@ -1183,7 +1267,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) + std::async(std::launch::async, build_build_script, build_script, stuff.cppfront_include_dir) ); } @@ -1330,7 +1414,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) + std::async(std::launch::async, build_binary, bin, stuff.cppfront_include_dir) ); } @@ -1593,6 +1677,7 @@ cpp2b_source_binary_info: @struct type = { cpp2b_source_build_info: @struct type = { src: fs::path = (); imports: std::vector = (); + constants: std::map = (); } cpp2b_source_unknown_info: @struct type = { @@ -1683,91 +1768,43 @@ cpp2b_parse_cpp1_module_statements: (copy stream: std::ifstream) -> (module_name } cpp2b_parse_source: (source_file: fs::path) -> cpp2b_source_info = { - stream: std::ifstream = source_file; - statement: std::string = ""; - imports: std::vector = (); - - // without this cppfront thinks stream is never used - assert(stream); - - result: cpp2b_source_info = (); - result.set_unknown(cpp2b_source_unknown_info(source_file)); - - while std::getline(stream, statement, ';') { - trimmed_statement := statement.ltrim(); - kw_import: std::string_view = "import"; + info := cpp2b_build_info_parser::parse_source(source_file.string()); - if trimmed_statement.starts_with(kw_import) { - import_name := trimmed_statement.lstrip(kw_import.size()).ltrim().rtrim(); - if !contains_any_whitespace(import_name) { - imports.emplace_back(import_name); - } - } + if std::ranges::find(info.imports, "std") == info.imports.end() { + info.imports.emplace_back("std"); } - if std::ranges::find(imports, "std") == imports.end() { - imports.emplace_back("std"); + if std::ranges::find(info.imports, "std.compat") == info.imports.end() { + info.imports.emplace_back("std.compat"); } - if std::ranges::find(imports, "std.compat") == imports.end() { - imports.emplace_back("std.compat"); - } + result: cpp2b_source_info = (); + result.set_unknown(cpp2b_source_unknown_info(source_file)); - if source_file.filename() == "build.cpp2" { + if info.kind == cpp2b_build_info_parser::source_kind::build { build_info: cpp2b_source_build_info = (); build_info.src = source_file; - build_info.imports = imports; + build_info.imports = info.imports; + build_info.constants = info.constants; result.set_build(build_info); return result; } - stream.clear(); - stream.seekg(0); - - while std::getline(stream, statement, ';') { - trimmed_statement := statement.ltrim(); - kw_export: std::string_view = "export"; - kw_module: std::string_view = "module"; - - if trimmed_statement.starts_with(kw_export) { - trimmed_statement = trimmed_statement.lstrip(kw_export.size()).ltrim(); - if !trimmed_statement.starts_with(kw_module) { break; } - - module_name := trimmed_statement.lstrip(kw_module.size()).ltrim(); - partition_start := module_name.find(':'); - if partition_start != std::string::npos { - module_name = module_name.substr(0, partition_start).rtrim(); - } else { - module_name = module_name.rtrim(); - } - - mod_info: cpp2b_source_module_info = (); - - mod_info.name = std::string(module_name); - mod_info.imports = imports; - mod_info.exported = true; - - result.set_mod(mod_info); - return result; - } + if info.kind == cpp2b_build_info_parser::source_kind::module { + mod_info: cpp2b_source_module_info = (); + mod_info.name = info.module_name; + mod_info.imports = info.imports; + mod_info.exported = info.exported; + result.set_mod(mod_info); + return result; } - stream.clear(); - stream.seekg(0); - - line: std::string = (); - while std::getline(stream, line) { - trimmed_line := line.ltrim(); - if trimmed_line.starts_with("main") { - after_main := trimmed_line.substr(4).ltrim(); - if after_main.empty() || after_main[0] == ':' { - bin_info: cpp2b_source_binary_info = (); - bin_info.src = source_file; - bin_info.imports = imports; - result.set_bin(bin_info); - return result; - } - } + if info.kind == cpp2b_build_info_parser::source_kind::binary { + bin_info: cpp2b_source_binary_info = (); + bin_info.src = source_file; + bin_info.imports = info.imports; + result.set_bin(bin_info); + return result; } return result; diff --git a/typos.toml b/typos.toml index cec9439..c179e70 100644 --- a/typos.toml +++ b/typos.toml @@ -1,6 +1,7 @@ [files] extend-exclude = [ "src/nlohmann.json.cppm", + "cppfront", ] [default]