diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9caf86d76..1d0d4843b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -123,14 +123,20 @@ jobs: mdbook build docs/manual-zh nightly-test: + name: nightly-test (${{ matrix.os }} / msvc:${{ matrix.msvc }}) needs: [typo-check, license-header-check] strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest + include: + - os: ubuntu-latest + msvc: disabled + - os: macos-latest + msvc: disabled + - os: windows-latest + msvc: enabled + - os: windows-latest + msvc: disabled runs-on: ${{ matrix.os }} steps: - name: Configure git @@ -163,7 +169,7 @@ jobs: "$env:GITHUB_WORKSPACE\target\debug" | Out-File -FilePath $env:GITHUB_PATH -Append - name: Setup MSVC - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.os == 'windows-latest' && matrix.msvc == 'enabled' }} uses: ilammy/msvc-dev-cmd@v1 - name: Build @@ -249,14 +255,20 @@ jobs: cargo run --bin moon -- -Z rupes_recta -C ~/.moon/lib/core build --target wasm-gc stable-test: + name: stable-test (${{ matrix.os }} / msvc:${{ matrix.msvc }}) needs: [typo-check, license-header-check] strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest + include: + - os: ubuntu-latest + msvc: disabled + - os: macos-latest + msvc: disabled + - os: windows-latest + msvc: enabled + - os: windows-latest + msvc: disabled runs-on: ${{ matrix.os }} steps: - name: Configure git @@ -286,7 +298,7 @@ jobs: "C:\Users\runneradmin\.moon\bin" | Out-File -FilePath $env:GITHUB_PATH -Append - name: Setup MSVC - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.os == 'windows-latest' && matrix.msvc == 'enabled' }} uses: ilammy/msvc-dev-cmd@v1 - name: Build @@ -350,6 +362,60 @@ jobs: cargo run --bin moon -- -C "$env:USERPROFILE\.moon\lib\core" test --release --target native cargo run --bin moon -- -C "$env:USERPROFILE\.moon\lib\core" test --doc + windows-tcc-first-test: + name: windows tcc first test (${{ matrix.msvc }}) + needs: [typo-check, license-header-check] + strategy: + fail-fast: false + matrix: + msvc: + - enabled + - disabled + runs-on: windows-latest + steps: + - name: Configure git + run: git config --global core.autocrlf false + + - uses: actions/checkout@v4 + with: + submodules: true + + - uses: dtolnay/rust-toolchain@1.90.0 + with: + components: rustfmt, clippy + + - name: Cargo cache + uses: Swatinem/rust-cache@v2 + + - name: install MoonBit(Windows) + run: | + $env:MOONBIT_INSTALL_VERSION="nightly" + Set-ExecutionPolicy RemoteSigned -Scope CurrentUser; irm https://cli.moonbitlang.com/install/powershell.ps1 | iex + "$env:USERPROFILE\.moon\bin" | Out-File -FilePath $env:GITHUB_PATH -Append + "$env:GITHUB_WORKSPACE\target\debug" | Out-File -FilePath $env:GITHUB_PATH -Append + + - name: Setup MSVC + if: ${{ matrix.msvc == 'enabled' }} + uses: ilammy/msvc-dev-cmd@v1 + + - name: Build moon binary + run: cargo build -p moon --bin moon + + - name: Set built binary to PATH (Windows) + run: Add-Content $env:GITHUB_PATH "$env:GITHUB_WORKSPACE\target\debug" + + - name: Bundle core (Windows) + run: cargo run --bin moon -- -C "$env:USERPROFILE\.moon\lib\core" bundle --all + + - name: Verify windows tcc-run default gate + run: cargo test -p moon --test mod test_cases::native_backend::tcc_run::test_native_backend_tcc_run_windows_disabled_by_default -- --exact + + - name: Verify windows tcc-run experimental path + run: cargo test -p moon --test mod test_cases::native_backend::tcc_run::test_native_backend_tcc_run_windows_experimental -- --exact + + - name: Verify windows tcc-run with MOON_CC=tcc (msvc runtime) + run: cargo test -p moon --test mod test_cases::native_backend::tcc_run::test_native_backend_tcc_run_windows_with_env_tcc_cc_uses_msvc_runtime -- --exact + # coverage: # needs: bleeding-test # runs-on: macos-latest diff --git a/crates/moon/src/rr_build/mod.rs b/crates/moon/src/rr_build/mod.rs index 15ae0c5ee..1d35b039d 100644 --- a/crates/moon/src/rr_build/mod.rs +++ b/crates/moon/src/rr_build/mod.rs @@ -530,8 +530,20 @@ fn check_tcc_availability( return false; } - // Check platform availability - if !(cfg!(target_os = "linux") || cfg!(target_os = "macos")) { + // Check platform availability. + // + // On Windows this is currently experimental and opt-in only, so we can + // collect first-round test signals before enabling it by default. + if cfg!(target_os = "windows") { + let enabled = std::env::var("MOON_ENABLE_WINDOWS_TCC_RUN") + .is_ok_and(|x| matches!(x.as_str(), "1" | "true" | "TRUE" | "yes" | "on")); + if !enabled { + info!( + "Disabling `tcc -run`: Windows support is experimental (set MOON_ENABLE_WINDOWS_TCC_RUN=1 to enable)" + ); + return false; + } + } else if !(cfg!(target_os = "linux") || cfg!(target_os = "macos")) { info!("`tcc -run` is only supported on Linux and macOS"); return false; } diff --git a/crates/moon/tests/test_cases/native_backend/tcc_run.rs b/crates/moon/tests/test_cases/native_backend/tcc_run.rs index f3a635ec4..82f0c7f96 100644 --- a/crates/moon/tests/test_cases/native_backend/tcc_run.rs +++ b/crates/moon/tests/test_cases/native_backend/tcc_run.rs @@ -2,6 +2,8 @@ use crate::TestDir; use expect_test::expect_file; use super::assert_native_backend_graph_no_env; +#[cfg(windows)] +use crate::get_stdout_with_envs; #[test] #[cfg(unix)] @@ -21,3 +23,59 @@ fn test_native_backend_tcc_run() { expect_file!["tcc_run/test_native_linux_graph.jsonl.snap"], ); } + +#[test] +#[cfg(windows)] +fn test_native_backend_tcc_run_windows_disabled_by_default() { + let dir = TestDir::new("native_backend/tcc_run"); + let out = get_stdout_with_envs( + &dir, + ["test", "--target", "native", "--dry-run", "--sort-input"], + [] as [(&str, &str); 0], + ); + assert!( + !out.contains("write-tcc-rsp-file"), + "unexpected tcc-run graph on Windows without opt-in:\n{out}" + ); +} + +#[test] +#[cfg(windows)] +fn test_native_backend_tcc_run_windows_experimental() { + let dir = TestDir::new("native_backend/tcc_run"); + let out = get_stdout_with_envs( + &dir, + ["test", "--target", "native", "--dry-run", "--sort-input"], + [("MOON_ENABLE_WINDOWS_TCC_RUN", "1")], + ); + assert!( + out.contains("write-tcc-rsp-file"), + "expected tcc-run graph on Windows experimental path:\n{out}" + ); +} + +#[test] +#[cfg(windows)] +fn test_native_backend_tcc_run_windows_with_env_tcc_cc_uses_msvc_runtime() { + let dir = TestDir::new("native_backend/tcc_run"); + let out = get_stdout_with_envs( + &dir, + ["test", "--target", "native", "--dry-run", "--sort-input"], + [ + ("MOON_ENABLE_WINDOWS_TCC_RUN", "1"), + ("MOON_CC", "x86_64-unknown-fake_os-fake_libc-tcc"), + ], + ); + assert!( + out.contains("write-tcc-rsp-file"), + "expected tcc-run graph on Windows with MOON_CC=tcc:\n{out}" + ); + assert!( + !out.contains("-lm"), + "unexpected math library linkage for Windows tcc msvc-runtime path:\n{out}" + ); + assert!( + out.contains("libruntime.lib"), + "expected msvc-style runtime import lib for Windows tcc path:\n{out}" + ); +} diff --git a/crates/moon/tests/test_cases/prebuild_config_script/mod.rs b/crates/moon/tests/test_cases/prebuild_config_script/mod.rs index cceada109..553a09ea2 100644 --- a/crates/moon/tests/test_cases/prebuild_config_script/mod.rs +++ b/crates/moon/tests/test_cases/prebuild_config_script/mod.rs @@ -35,14 +35,22 @@ fn test_prebuild_config_common(dir: TestDir) { assert!(line.contains("-l______this_is_added_by_config_script_______")); assert!(line.contains("-lmylib")); assert!(line.contains("-L/my-search-path")); - } else if line.contains("cl.exe") // cl.exe might be quoted - && line.contains("/Fe./_build/native/debug/build/main/main.exe") - && cfg!(windows) - { - found_link_flags.set(()).expect("final linking found twice"); - assert!(line.contains("-l______this_is_added_by_config_script_______")); - assert!(line.contains("mylib")); - assert!(line.contains("/LIBPATH:/my-search-path")); + } else if cfg!(windows) { + let is_msvc_link = line.contains("cl.exe") // cl.exe might be quoted + && line.contains("/Fe./_build/native/debug/build/main/main.exe"); + let is_gnu_link = line.contains("-o ./_build/native/debug/build/main/main.exe") + && (line.contains("cc ") || line.contains("cc.exe")); + if is_msvc_link || is_gnu_link { + found_link_flags.set(()).expect("final linking found twice"); + assert!(line.contains("-l______this_is_added_by_config_script_______")); + if is_msvc_link { + assert!(line.contains("mylib")); + assert!(line.contains("/LIBPATH:/my-search-path")); + } else { + assert!(line.contains("-lmylib")); + assert!(line.contains("-L/my-search-path")); + } + } } } found_c_flags_replacement diff --git a/crates/moon/tests/test_cases/prebuild_link_config_self/mod.rs b/crates/moon/tests/test_cases/prebuild_link_config_self/mod.rs index d25853895..6ac09c5c5 100644 --- a/crates/moon/tests/test_cases/prebuild_link_config_self/mod.rs +++ b/crates/moon/tests/test_cases/prebuild_link_config_self/mod.rs @@ -17,14 +17,22 @@ fn test_prebuild_link_config_self() { assert!(line.contains("-l__prebuild_self_link_flag__")); assert!(line.contains("-lprebuildselflib")); assert!(line.contains("-L/prebuild-self-path")); - } else if line.contains("cl.exe") - && line.contains("/Fe./_build/native/debug/build/main/main.exe") - && cfg!(windows) - { - found_final_link.set(()).expect("final linking found twice"); - assert!(line.contains("-l__prebuild_self_link_flag__")); - assert!(line.contains("prebuildselflib")); - assert!(line.contains("/LIBPATH:/prebuild-self-path")); + } else if cfg!(windows) { + let is_msvc_link = line.contains("cl.exe") + && line.contains("/Fe./_build/native/debug/build/main/main.exe"); + let is_gnu_link = line.contains("-o ./_build/native/debug/build/main/main.exe") + && (line.contains("cc ") || line.contains("cc.exe")); + if is_msvc_link || is_gnu_link { + found_final_link.set(()).expect("final linking found twice"); + assert!(line.contains("-l__prebuild_self_link_flag__")); + if is_msvc_link { + assert!(line.contains("prebuildselflib")); + assert!(line.contains("/LIBPATH:/prebuild-self-path")); + } else { + assert!(line.contains("-lprebuildselflib")); + assert!(line.contains("-L/prebuild-self-path")); + } + } } } diff --git a/crates/moonutil/src/compiler_flags.rs b/crates/moonutil/src/compiler_flags.rs index e3a9d8267..ff53873c5 100644 --- a/crates/moonutil/src/compiler_flags.rs +++ b/crates/moonutil/src/compiler_flags.rs @@ -537,12 +537,24 @@ fn add_linker_common_libraries>( config: &LinkerConfig

, ) { if cc.is_gcc_like() { - if cc.is_full_featured_gcc_like() { + if should_link_math_library(cc) { buf.push("-lm".to_string()); } if let Some(dyn_lib_path) = config.link_shared_runtime.as_ref() { - buf.push("-lruntime".to_string()); - buf.push(format!("-Wl,-rpath,{}", dyn_lib_path.as_ref().display())); + if should_use_msvc_style_tcc_runtime_linking(cc) { + buf.push( + dyn_lib_path + .as_ref() + .join("libruntime.lib") + .display() + .to_string(), + ); + } else { + buf.push("-lruntime".to_string()); + if should_add_runtime_rpath() { + buf.push(format!("-Wl,-rpath,{}", dyn_lib_path.as_ref().display())); + } + } } } } @@ -824,11 +836,29 @@ fn add_cc_moonbitrun_with_warnings(cc: &CC, buf: &mut Vec, config: &CCCo } fn add_cc_common_libraries(cc: &CC, buf: &mut Vec, config: &CCConfig) { - if cc.is_full_featured_gcc_like() && config.output_ty != OutputType::Object { + if should_link_math_library(cc) && config.output_ty != OutputType::Object { buf.push("-lm".to_string()); } } +fn should_link_math_library(cc: &CC) -> bool { + if cfg!(target_os = "windows") && cc.is_tcc() { + // Windows tcc path is modeled as MSVC-style linking only. + false + } else { + cc.is_full_featured_gcc_like() + } +} + +fn should_use_msvc_style_tcc_runtime_linking(cc: &CC) -> bool { + cfg!(target_os = "windows") && cc.is_tcc() +} + +fn should_add_runtime_rpath() -> bool { + // PE/COFF loaders do not use ELF rpath semantics. + !cfg!(target_os = "windows") +} + fn add_cc_msvc_linker_flags(cc: &CC, buf: &mut Vec, config: &CCConfig, lpath: &str) { if cc.is_msvc() && config.output_ty != OutputType::Object { buf.push("/link".to_string()); @@ -904,3 +934,32 @@ where buf } + +#[cfg(all(test, windows))] +mod tests { + use super::*; + + #[test] + fn windows_tcc_does_not_link_math_library() { + let cc = CC { + cc_kind: CCKind::Tcc, + cc_path: "tcc".to_string(), + ar_kind: ARKind::TccAr, + ar_path: "tcc".to_string(), + is_env_override: false, + }; + assert!(!should_link_math_library(&cc)); + } + + #[test] + fn windows_tcc_runtime_link_style_is_msvc() { + let cc = CC { + cc_kind: CCKind::Tcc, + cc_path: "tcc".to_string(), + ar_kind: ARKind::TccAr, + ar_path: "tcc".to_string(), + is_env_override: false, + }; + assert!(should_use_msvc_style_tcc_runtime_linking(&cc)); + } +}