diff --git a/.github/workflows/CI.yml b/.github/workflows/cmake.yml similarity index 99% rename from .github/workflows/CI.yml rename to .github/workflows/cmake.yml index 8bacdad6..bd3554e1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/cmake.yml @@ -1,4 +1,4 @@ -name: CI +name: cmake on: push: diff --git a/.github/workflows/xmake.yml b/.github/workflows/xmake.yml new file mode 100644 index 00000000..44d26a3d --- /dev/null +++ b/.github/workflows/xmake.yml @@ -0,0 +1,68 @@ +name: xmake + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + paths-ignore: + - '.clang-format' + - '.clangd' + - '.gitignore' + - 'LICENSE' + - 'README.md' + +jobs: + build: + strategy: + matrix: + os: [ubuntu-24.04, windows-2022] + + runs-on: ${{ matrix.os }} + + concurrency: + group: ${{ github.ref }}-${{ github.base_ref }}-${{ github.head_ref }}-${{ matrix.os }} + cancel-in-progress: false + + steps: + - name: Setup llvm + if: matrix.os == 'windows-2022' + uses: MinoruSekine/setup-scoop@v4.0.1 + with: + buckets: main + apps: llvm + + - name: Setup llvm & libstdc++ & cmake & ninja + if: matrix.os == 'ubuntu-24.04' + run: | + sudo apt update + sudo apt install -y software-properties-common + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt update + sudo apt install -y gcc-14 g++-14 libstdc++-14-dev + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 100 + sudo update-alternatives --set gcc /usr/bin/gcc-14 + sudo update-alternatives --set g++ /usr/bin/g++-14 + + wget -O - https://apt.llvm.org/llvm.sh | sudo bash + sudo apt install -y llvm-18 llvm-18-tools clang-18 libclang-rt-18-dev lld-18 + sudo ln -s /usr/bin/llvm-ar-18 /usr/bin/llvm-ar + sudo ln -s /usr/bin/llvm-ranlib-18 /usr/bin/llvm-ranlib + + sudo apt install -y cmake ninja-build + + - name: Setup xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: '2.9.7' + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build clice + run: | + xmake --yes --diagnosis + + - name: Run tests + run: xmake test --verbose diff --git a/.gitignore b/.gitignore index 6a8fd376..5da889f3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,5 @@ temp* /build* notes temp/ - +.xmake/ diff --git a/src/Basic/SourceConverter.cpp b/src/Basic/SourceConverter.cpp index 9a2c091c..259f7987 100644 --- a/src/Basic/SourceConverter.cpp +++ b/src/Basic/SourceConverter.cpp @@ -1,5 +1,7 @@ -#include "Basic/SourceConverter.h" #include "Basic/Location.h" +#include "Basic/SourceConverter.h" +#include "Support/Support.h" +#include "clang/Basic/SourceManager.h" namespace clice { @@ -189,6 +191,9 @@ proto::DocumentUri SourceConverter::toURI(llvm::StringRef fspath) { std::terminate(); llvm::SmallString<128> path("file://"); +#if defined(_WIN32) + path.append("/"); +#endif for(auto c: fspath) { if(c == '\\') { @@ -218,20 +223,29 @@ proto::DocumentUri SourceConverter::toURI(llvm::StringRef fspath) { std::string SourceConverter::toPath(llvm::StringRef uri) { llvm::StringRef cloned = uri; - auto pos = cloned.find(':'); - if(pos == llvm::StringRef::npos) - std::terminate(); - - auto scheme = cloned.substr(0, pos); - cloned = cloned.substr(pos + 1); - - if(cloned.consume_front("//")) - cloned = cloned.substr(cloned.find('/')); +#if defined(_WIN32) + if (cloned.starts_with("file:///")) { + cloned = cloned.drop_front(8); + } else { + std::terminate(); + } +#elif defined(__unix__) + if (cloned.starts_with("file://")) { + cloned = cloned.drop_front(7); + } else { + std::terminate(); + } +#else +#error "Unsupported platform" +#endif auto decoded = decodePercent(cloned); + llvm::SmallString<128> result; - if(auto err = fs::real_path(decoded, result)) + if(auto err = fs::real_path(decoded, result)){ + print("Failed to get real path: {}, Input is {}\n", err.message(), decoded); std::terminate(); + } return result.str().str(); } diff --git a/src/Driver/clice.cc b/src/Driver/clice.cc index 6b326a69..846b614a 100644 --- a/src/Driver/clice.cc +++ b/src/Driver/clice.cc @@ -12,6 +12,8 @@ llvm::cl::opt config("config", llvm::cl::opt pipe("pipe", llvm::cl::desc("Use pipe mode")); +llvm::cl::opt resource_dir("resource-dir", llvm::cl::desc("Resource dir path")); + } // namespace cl int main(int argc, const char** argv) { @@ -30,9 +32,13 @@ int main(int argc, const char** argv) { } /// Get the resource directory. - if(auto error = fs::init_resource_dir(argv[0])) { - log::fatal("Failed to get resource directory, because {0}", error); - return 1; + if(!cl::resource_dir.empty()) { + fs::resource_dir = cl::resource_dir.getValue(); + } else { + if(auto error = fs::init_resource_dir(argv[0])) { + log::fatal("Failed to get resource directory, because {0}", error); + return 1; + } } static Server server; diff --git a/src/Driver/unit_tests.cc b/src/Driver/unit_tests.cc index 4af1a8f2..4127da23 100644 --- a/src/Driver/unit_tests.cc +++ b/src/Driver/unit_tests.cc @@ -12,7 +12,7 @@ llvm::cl::opt test_dir("test-dir", llvm::cl::value_desc("path"), llvm::cl::Required); -llvm::SmallString<128> resource_dir; +llvm::cl::opt resource_dir("resource-dir", llvm::cl::desc("Resource dir path")); } // namespace cl @@ -33,14 +33,18 @@ llvm::StringRef resource_dir() { int main(int argc, char** argv) { using namespace clice; - if(auto error = fs::init_resource_dir(argv[0])) { - llvm::outs() << std::format("Failed to get resource directory, because {}\n", error); - return 1; - } - testing::InitGoogleTest(&argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv, "clice test\n"); + if(!cl::resource_dir.empty()) { + fs::resource_dir = cl::resource_dir.getValue(); + } else { + if(auto error = fs::init_resource_dir(argv[0])) { + llvm::outs() << std::format("Failed to get resource directory, because {}\n", error); + return 1; + } + } + return RUN_ALL_TESTS(); } diff --git a/unittests/Basic/SourceConverter.cpp b/unittests/Basic/SourceConverter.cpp index 9e5702ed..c86cc134 100644 --- a/unittests/Basic/SourceConverter.cpp +++ b/unittests/Basic/SourceConverter.cpp @@ -1,93 +1,91 @@ -#include "Test/Test.h" #include "Basic/SourceConverter.h" +#include "Test/Test.h" namespace clice { namespace { TEST(SourceConverter, Remeasure) { - using SC = SourceConverter; + using SC = SourceConverter; - SourceConverter utf8{proto::PositionEncodingKind::UTF8}; + SourceConverter utf8{proto::PositionEncodingKind::UTF8}; - EXPECT_EQ(utf8.remeasure(""), 0); - EXPECT_EQ(utf8.remeasure("ascii"), 5); + EXPECT_EQ(utf8.remeasure(""), 0); + EXPECT_EQ(utf8.remeasure("ascii"), 5); - SourceConverter utf16{proto::PositionEncodingKind::UTF16}; + SourceConverter utf16{proto::PositionEncodingKind::UTF16}; - EXPECT_EQ(utf16.remeasure("↓"), 1); - EXPECT_EQ(utf16.remeasure("¥"), 1); + EXPECT_EQ(utf16.remeasure("↓"), 1); + EXPECT_EQ(utf16.remeasure("¥"), 1); - SourceConverter utf32{proto::PositionEncodingKind::UTF32}; - EXPECT_EQ(utf8.remeasure("😂"), 4); - EXPECT_EQ(utf16.remeasure("😂"), 2); - EXPECT_EQ(utf32.remeasure("😂"), 1); + SourceConverter utf32{proto::PositionEncodingKind::UTF32}; + EXPECT_EQ(utf8.remeasure("😂"), 4); + EXPECT_EQ(utf16.remeasure("😂"), 2); + EXPECT_EQ(utf32.remeasure("😂"), 1); } TEST(SourceConverter, Position) { - const char* main = "int a /*😂*/ = 1;$(eof)"; + const char *main = "int a /*😂*/ = 1;$(eof)"; - Tester txs("main.cpp", main); - txs.run("-std=c++11"); + Tester txs("main.cpp", main); + txs.run("-std=c++11"); - auto& src = txs.info.srcMgr(); - auto& tks = txs.info.tokBuf(); + auto &src = txs.info.srcMgr(); + auto &tks = txs.info.tokBuf(); - auto mainid = src.getMainFileID(); - auto tokens = - tks.expandedTokens({src.getLocForStartOfFile(mainid), src.getLocForEndOfFile(mainid)}); + auto mainid = src.getMainFileID(); + auto tokens = tks.expandedTokens( + {src.getLocForStartOfFile(mainid), src.getLocForEndOfFile(mainid)}); - auto eof = tokens.back().endLocation(); - txs.expect("eof", eof); + auto eof = tokens.back().endLocation(); + txs.expect("eof", eof); - { - SourceConverter cvtr{proto::PositionEncodingKind::UTF8}; - auto pos = cvtr.toPosition(eof, src); - EXPECT_EQ(pos.line, 0); - EXPECT_EQ(pos.character, 19); - } + { + SourceConverter cvtr{proto::PositionEncodingKind::UTF8}; + auto pos = cvtr.toPosition(eof, src); + EXPECT_EQ(pos.line, 0); + EXPECT_EQ(pos.character, 19); + } - { - SourceConverter cvtr{proto::PositionEncodingKind::UTF16}; - auto pos = cvtr.toPosition(eof, src); - EXPECT_EQ(pos.line, 0); - EXPECT_EQ(pos.character, 19); - } + { + SourceConverter cvtr{proto::PositionEncodingKind::UTF16}; + auto pos = cvtr.toPosition(eof, src); + EXPECT_EQ(pos.line, 0); + EXPECT_EQ(pos.character, 19); + } - { - SourceConverter cvtr{proto::PositionEncodingKind::UTF32}; - auto pos = cvtr.toPosition(eof, src); - EXPECT_EQ(pos.line, 0); - EXPECT_EQ(pos.character, 19); - } + { + SourceConverter cvtr{proto::PositionEncodingKind::UTF32}; + auto pos = cvtr.toPosition(eof, src); + EXPECT_EQ(pos.line, 0); + EXPECT_EQ(pos.character, 19); + } } TEST(SourceConverter, UriAndFsPath) { - using SC = SourceConverter; + using SC = SourceConverter; - // It must be a existed file. -#ifdef unix - const char* fspath[] = {"/dev/null"}; - const char* uri[] = {"file:///dev/null"}; + // It must be a existed file. +#if defined(__unix__) + llvm::StringRef paths[] = {"/dev/null"}; + llvm::StringRef uris[] = {"file:///dev/null"}; +#elif defined(_WIN32) + llvm::StringRef paths[] = {"C:\\Windows\\System32\\notepad.exe"}; + llvm::StringRef uris[] = {"file:///C%3A/Windows/System32/notepad.exe"}; #else - const char* fspath[] = {}; - const char* uri[] = {}; +#error "Unsupported platform" #endif - EXPECT_EQ(std::size(fspath), std::size(uri)); - for(int i = 0; i < std::size(fspath); ++i) { - auto fspath_ = fspath[i]; - auto uri_ = uri[i]; + EXPECT_EQ(std::size(paths), std::size(uris)); - auto uri1 = SC::toURI(fspath_); - EXPECT_EQ(uri1, uri_); - - auto fspath2 = SC::toPath(uri_); - EXPECT_EQ(fspath2, fspath_); - } + for (int i = 0; i < std::size(paths); ++i) { + llvm::outs() << ": " << SC::toURI(paths[i])<< "\n"; + llvm::outs() << ": " << SC::toPath(uris[i])<< "\n"; + EXPECT_EQ(SC::toURI(paths[i]), uris[i]); + EXPECT_EQ(paths[i], SC::toPath(uris[i])); + } } -} // namespace - -} // namespace clice +} // namespace +} // namespace clice diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 00000000..8780f2fd --- /dev/null +++ b/xmake.lua @@ -0,0 +1,108 @@ +set_xmakever("2.9.7") +set_project("clice") + +set_allowedplats("windows", "linux") +set_allowedmodes("debug", "release") + +option("enable_test", {default = true}) +option("dev", {default = true}) + +if has_config("dev") then + set_policy("compatibility.version", "3.0") + if is_plat("windows") then + set_runtimes("MD") + elseif is_plat("linux") and is_mode("debug") then + set_policy("build.sanitizer.address", true) + end + + if has_config("enable_test") then + add_requires("gtest[main]") + end +end + +add_requires("toml++", "libuv") +add_requires("llvm") + +add_rules("mode.release", "mode.debug") +set_languages("c++23") +add_rules("clice_build_config") + +target("clice-core") + set_kind("$(kind)") + add_files("src/**.cpp|Driver/*.cpp") + set_pcxxheader("include/Compiler/Clang.h") + add_includedirs("include", {public = true}) + + add_packages("llvm", "libuv", {public = true}) + +target("clice") + set_kind("binary") + add_files("src/Driver/clice.cc") + + add_deps("clice-core") + +target("integration_tests") + set_default(false) + set_kind("binary") + add_files("src/Driver/integration_tests.cc") + + add_deps("clice-core") + -- TODO + -- add_tests("integration_tests") + +target("unit_tests") + set_default(false) + set_kind("binary") + add_files("src/Driver/unit_tests.cc", "unittests/**.cpp") + + add_deps("clice-core") + add_packages("gtest") + + add_tests("default") + + on_config(function (target) + target:set("runargs", + "--test-dir=" .. path.absolute("tests"), + "--resource-dir=" .. path.join(target:dep("clice-core"):pkg("llvm"):installdir(), "lib/clang/20") + ) + end) + +rule("clice_build_config") + on_load(function (target) + target:set("toolchains", "clang") + + target:add("cxflags", "-fno-rtti", {tools = {"clang", "gcc"}}) + target:add("cxflags", "/GR-", {tools = {"clang_cl", "cl"}}) + target:set("exceptions", "no-cxx") + if target:is_plat("windows") then + target:add("ldflags", "-fuse-ld=lld-link") + elseif target:is_plat("linux") then + -- gnu ld will cause link order + target:add("ldflags", "-fuse-ld=lld") + end + end) + +package("llvm") + if is_plat("windows") then + add_urls("https://github.com/clice-project/llvm-binary/releases/download/$(version)/x64-windows-msvc-release.7z") + + add_versions("20.0.0", "6dc78ca012fa5cc365394b72371b7aaff24a5b1a92fc204ea634f9d360bef694") + elseif is_plat("linux") then + add_urls("https://github.com/clice-project/llvm-binary/releases/download/$(version)/x86_64-linux-gnu-release.tar.xz") + + add_versions("20.0.0", "4c8a52ee3ac8ae34e4fa4ae49a6488f6ff8454386ef0e5617dc90a09e8032f74") + end + + if is_plat("windows") then + add_configs("runtimes", {description = "Set compiler runtimes.", default = "MD", readonly = true}) + end + + if is_plat("windows", "mingw") then + add_syslinks("version", "ntdll") + end + + on_install(function (package) + os.mv("bin", package:installdir()) + os.mv("lib", package:installdir()) + os.mv("include", package:installdir()) + end)