烧饼论坛 商城 银行 彩票
登录

Windows 搭建 WSL2 AI 开发环境

667
  • 芒果
    UID 1010Lv.10 崭露头角超级版主博主dejavu.moe 编辑于

    cover.webp

    介绍我在 Windows 上使用 WSL2 打造 AI 开发环境的一些思路,源码和 AI Agent 留在 Windows ,WSL2 只当编译测试沙盒,用调度脚本和约束规则串联工作流
    原文链接:https://blog.dejavu.moe/posts/windows-wsl2-ai-dev-environment/

    前言

    最近把主力笔电换成 Omarchy Linux 4 用了将近一个月,纯键盘流加平铺式桌面管理器体验很好,开箱即用的 AI Naive OS 风格也不错。有些软件始终找不到替代品,又不想折腾双系统和虚拟机,最终还是回到了 Windows。

    前些年一直有「Windows 是最好的 Linux 发行版」的说法 [^1] ,但就我自己从 WSL1 到 WSL2 的使用体验来看,始终绕不开两个问题:

    1. 跨文件系统访问的 I/O 性能极其糟糕
    2. 宿主机 AI Agent 与 WSL2 交互麻烦

    WSL2 通过 /mnt 挂载 Windows 盘符,例如 /mnt/d 对应 D 盘。这种跨文件系统访问的 I/O 性能相比原生 ext4 文件系统性能损耗很大,涉及大量小文件时尤为明显。

    我的需求很简单:

    本地运行的 Codex、Claude Code 直接操作代码工作区,干净的代码完全存放在 Windows 宿主机磁盘,Git、SSH、GPG、YubiKey 不需要在 WSL2 里再配一套,WSL2 只充当无状态的编译与测试沙盒。Docker 镜像构建和测试走 Docker Desktop 的 WSL2 集成,而非在 WSL2 里单独安装 Docker 引擎。

    规范分工

    Windows 仓库是唯一权威源码,WSL2 内的副本随时可以清空重建。宿主机与 WSL2 之间靠一个 PowerShell Wrapper 脚本 linux-task.ps1 串联。Agent 自动化也这条路径:宿主机改完代码后,在 WSL2 的 ext4 内同步跑构建测试,日志与退出码传回 Windows。

    architecture.webp

    本文以我当前的实际环境为例,所有 WSL 虚拟磁盘、镜像与脚本统一放在 D:\WSL ,最终的文件目录树看起来像这样:

    D:\WSL
    ├── Debian/
    │   ├── ext4.vhdx    Debian 系统虚拟磁盘
    │   └── shortcut.ico
    ├── DockerDesktop/
    │   ├── disk/
    │   │   └── docker_data.vhdx    # 容器与镜像存储
    │   └── main/
    │       └── ext4.vhdx    # Docker Desktop 运行引擎
    ├── Images/
    │   └── Debian.wsl    # 发行版安装包
    ├── Scripts/
    │   └── linux-task.ps1    # 跨环境调度脚本
    ├── Snapshots/    # 冷备份快照
    └── swap.vhdx    # 全局交换分区
    

    环境准备

    下文假设 Windows 用户名为 ice ,Debian 用户名为 dejavu ,源码目录 D:\Forgejo ,操作时请替换为你的实际路径。

    启用 WSL2

    基础的 Scoop 与 Windows Terminal 配置可参考我之前的文章,本文不再展开。

    在 BIOS/UEFI 设置中启用 CPU 虚拟化,然后以管理员身份运行 PowerShell 启用相关功能后重启:

    $features = @(
        'VirtualMachinePlatform'
        'Microsoft-Windows-Subsystem-Linux'
    )
    foreach ($name in $features) {
        Enable-WindowsOptionalFeature -Online -FeatureName $name -All -NoRestart
    }
    Restart-Computer
    

    更新 WSL 内核并设置默认版本 [^2] :

    wsl --update
    wsl --set-default-version 2
    

    宿主机工具链

    Windows 宿主机的 CLI 工具由 Scoop 安装,多语言运行时用 mise 管理。在 $PROFILE 中加入 mise 激活代码:

    scoop install mise
    notepad $PROFILE
    

    加入 pwsh 配置:

    if (Get-Command mise -ErrorAction SilentlyContinue) {
        (&mise activate pwsh) | Out-String | Invoke-Expression
    }
    

    重新打开终端后安装宿主机全局运行时:

    mise use --global node@lts pnpm@latest [email protected] uv@latest
    mise use --global go@latest rust@stable gh@latest codex@latest
    

    调整 PATH 优先级

    PowerShell $PROFILE 仅对交互式终端生效,Codex 等 AI Agent 和 IDE 依赖系统持久 PATH。此时可执行文件经由 mise 管理,shims 位于 %LOCALAPPDATA%\mise\shims 。

    但 Windows 默认 PATH 存在优先级冲突,比如 WindowsApps 自带的占位 python.exe 排在前面,敲 python 会弹窗让你去 Microsoft Store 下载安装。我还发现 使用 YubiKey 原生 GPG 时,Scoop 安装的 GPG 也会被 Git 自带的 GPG 抢占。

    path-order.webp

    因此我需要运行以下脚本调整用户 PATH 优先级,将 mise\shims 提到 WindowsApps 前面,同时加入脚本目录 D:\WSL\Scripts :

    New-Item -ItemType Directory -Force -Path 'D:\WSL\Scripts' | Out-Null
    
    $userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
    $stamp = Get-Date -Format yyyyMMddHHmmss
    $userPath | Set-Content "$env:USERPROFILE\user-path-backup-$stamp.txt"
    
    $priorityDirs = @("$env:LOCALAPPDATA\mise\shims")
    $gpgBin = 'D:\Scoop\apps\gpg\current\bin'
    if (Test-Path -LiteralPath (Join-Path $gpgBin 'gpgconf.exe')) {
        $priorityDirs = @($gpgBin) + $priorityDirs
    }
    $rest = @($userPath -split ';' | Where-Object {
        $_ -and $priorityDirs -notcontains $_.TrimEnd('\')
    })
    $pathParts = @($priorityDirs + $rest)
    foreach ($dir in @("$env:USERPROFILE\.local\bin", 'D:\WSL\Scripts')) {
        if (($pathParts | ForEach-Object { $_.TrimEnd('\') }) -notcontains $dir) {
            $pathParts += $dir
        }
    }
    $newPath = $pathParts -join ';'
    [Environment]::SetEnvironmentVariable('Path', $newPath, 'User')
    

    改完后重启终端,验证效果:

    Get-Command python.exe -All | Select-Object Name,Source
    $py = (Get-Command python.exe).Source
    if ($py -notlike "*\mise\*") {
        throw "Python 没有命中 mise,当前来源:$py"
    }
    
    $gpgBin = 'D:\Scoop\apps\gpg\current\bin'
    if (Test-Path -LiteralPath (Join-Path $gpgBin 'gpgconf.exe')) {
        Get-Command gpg.exe,gpgconf.exe,gpg-connect-agent.exe -All | Select-Object Name,Source
        foreach ($name in @('gpg.exe', 'gpgconf.exe', 'gpg-connect-agent.exe')) {
            if ((Get-Command $name).Source -ine (Join-Path $gpgBin $name)) {
                throw "$name 没有命中原生 GPG,请检查 PATH 顺序"
            }
        }
    }
    

    安装 Debian WSL2

    把 Debian 13 的 WSL2 虚拟磁盘固定在 D:\WSL\Debian ,下载镜像离线安装:

    New-Item -ItemType Directory -Force -Path 'D:\WSL\Images' | Out-Null
    $repo = 'https://raw.githubusercontent.com/microsoft/WSL/master'
    $catalog = Invoke-RestMethod "$repo/distributions/DistributionInfo.json"
    $debian = @($catalog.ModernDistributions.Debian) | Where-Object Default | Select-Object -First 1
    
    $imagePath = 'D:\WSL\Images\Debian.wsl'
    Invoke-WebRequest -Uri $debian.Amd64Url.Url -OutFile $imagePath
    $hash = (Get-FileHash -LiteralPath $imagePath -Algorithm SHA256).Hash
    if ($hash -ine $debian.Amd64Url.Sha256) { throw 'SHA256 校验失败' }
    
    wsl --install --from-file $imagePath \`
        --location 'D:\WSL\Debian' --name Debian --version 2 --no-launch
    wsl --set-default Debian
    wsl -d Debian
    

    创建用户(如 dejavu )并配置权限:

    wsl -d Debian -u root -- bash -lc "apt update && apt install -y sudo && usermod -aG sudo dejavu"
    

    性能与网络配置

    编辑 WSL2 全局配置 %USERPROFILE%\.wslconfig ,限制资源,将 swap 指向 D 盘,启用镜像网络 [^3] :

    [wsl2]
    memory=12GB
    processors=8
    
    swap=4GB
    swapFile=D:\\WSL\\swap.vhdx
    
    networkingMode=mirrored
    
    nestedVirtualization=false
    

    WSL2 内部的 Debian 配置 /etc/wsl.conf :

    [boot]
    systemd=true
    
    [interop]
    enabled=true
    appendWindowsPath=false
    
    [user]
    default=dejavu
    

    appendWindowsPath=false 用于阻断 Debian 自动继承 Windows PATH,防止误调 .exe 。

    保留 enabled=true 是为了需要时仍能手动调用宿主程序。

    改完后运行 wsl --shutdown 使配置生效。

    Linux 工具链

    进入 Debian 更新系统并安装基础依赖:

    sudo apt update && sudo apt full-upgrade -y
    sudo apt install -y vim build-essential ca-certificates curl wget git \
      openssh-client gnupg unzip zip xz-utils jq file pkg-config procps \
      iproute2 dnsutils bubblewrap rsync python3 python3-venv python3-pip
    

    配置免密 sudo:

    echo "$USER ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/90-$USER-nopasswd
    sudo chmod 0440 /etc/sudoers.d/90-$USER-nopasswd
    

    安装 Linux 端 mise 工具链:

    curl -fsSL https://mise.run | sh
    cat >> ~/.bashrc <<'EOF'
    export PATH="$HOME/.local/bin:$HOME/.local/share/mise/shims:$PATH"
    EOF
    source ~/.bashrc
    mise use --global node@lts pnpm@latest go@latest rust@stable
    

    WSL 默认把宿主盘符挂载到 /mnt/c ,mise 向上查找时可能误读 Windows 用户目录下的配置。修改 ~/.config/mise/miserc.toml :

    ignored_config_paths = ["/mnt/c/Users/ice/.config/mise"]
    

    验证,确认列表中没有宿主机配置:

    cd /mnt/c/Users/ice && mise config
    

    Docker 集成

    Docker 采用 Docker Desktop 的 WSL2 后端,WSL2 Debian 中不再单独安装。默认启用闲时自动休眠 (Resource Saver),宿主与子系统共享同一镜像库。

    安装 Docker Desktop 并将数据目录指向 D 盘:

    New-Item -ItemType Directory -Force -Path 'D:\WSL\DockerDesktop' | Out-Null
    $installer = Join-Path $env:TEMP 'DockerDesktopInstaller.exe'
    $url = 'https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe'
    Invoke-WebRequest $url -OutFile $installer
    Start-Process $installer -Wait -ArgumentList @(
        'install', '--user', '--backend=wsl-2', '--no-windows-containers',
        '--wsl-default-data-root=D:\WSL\DockerDesktop'
    )
    

    在 Docker Desktop 设置 Resources → WSL Integration 中勾选 Debian。集成后 Docker CLI 会自动挂载到 Linux,在 Debian 内把用户加入 docker 组即可:

    sudo groupadd -f docker
    sudo usermod -aG docker "$USER"
    

    新开终端,验证 Docker CLI 挂载路径、服务端连接,并运行测试容器:

    readlink -f "$(command -v docker)"
    # /mnt/wsl/docker-desktop/cli-tools/usr/bin/docker
    docker version          # Server 一栏显示 Docker Desktop
    docker run --rm hello-world
    

    跨环境调度器

    Windows 端的 linux-task.ps1 负责派发任务,Debian 端的 ~/.local/bin/linux-task 负责执行。

    runner-flow.webp

    脚本提供五种模式:

    模式运行位置功能说明
    build~/Build/<proj>-<hash>增量同步源码至 ext4 副本,执行依赖安装、编译与测试
    direct/mnt/<disk>/...轻量只读检查(如单文件 lint、格式校验)
    sync~/Build/<proj>-<hash>仅增量同步源码,不执行构建命令
    pathN/A输出当前项目在 Linux 中对应的副本路径
    clean~/Build/<proj>-<hash>删除该项目的 Linux 副本及依赖缓存

    Linux 执行端

    保存为 ~/.local/bin/linux-task 并赋予可执行权限:

    mkdir -p ~/.local/bin ~/Build
    cat > ~/.local/bin/linux-task <<'EOF'
    #!/usr/bin/env bash
    set -Eeuo pipefail
    
    usage() {
        cat <<'USAGE'
    Usage:
      linux-task direct <source-dir> <command>
      linux-task build  <source-dir> <command>
      linux-task sync   <source-dir>
      linux-task path   <source-dir>
      linux-task clean  <source-dir>
    USAGE
    }
    
    mode="${1:-}"
    src="${2:-}"
    cmd="${3:-}"
    
    case "$mode" in
        direct|build|sync|path|clean)
            ;;
        *)
            usage
            exit 2
            ;;
    esac
    
    if [[ -z "$src" ]]; then
        echo "Missing source directory." >&2
        exit 2
    fi
    
    if [[ ! -d "$src" ]]; then
        echo "Source directory does not exist: $src" >&2
        exit 2
    fi
    
    src="$(realpath "$src")"
    
    project_name="$(basename "$src")"
    project_hash="$(printf '%s' "$src" | sha256sum | cut -c1-12)"
    
    dst="$HOME/Build/${project_name}-${project_hash}"
    mise="$HOME/.local/bin/mise"
    
    if [[ ! -x "$mise" ]]; then
        echo "mise not found: $mise" >&2
        exit 1
    fi
    
    sync_project() {
        mkdir -p "$dst"
    
        echo "==> Sync"
        echo "Source: $src"
        echo "Build : $dst"
    
        rsync -rlt \
            --delete \
            --delete-delay \
            --exclude='.git/' \
            --exclude='node_modules/' \
            --exclude='target/' \
            --exclude='.next/' \
            --exclude='.turbo/' \
            --exclude='.cache/' \
            --exclude='.venv/' \
            --exclude='venv/' \
            --exclude='__pycache__/' \
            --exclude='.pytest_cache/' \
            --exclude='.mypy_cache/' \
            --exclude='.ruff_cache/' \
            --exclude='.wsl-runner-meta' \
            "$src/" "$dst/"
    
        rm -rf -- "$dst/.git"
        printf 'source=%s\nbuild=%s\n' \
            "$src" "$dst" > "$dst/.wsl-runner-meta"
    }
    
    case "$mode" in
        path)
            printf '%s\n' "$dst"
            ;;
    
        sync)
            sync_project
            ;;
    
        clean)
            case "$dst" in
                "$HOME"/Build/*)
                    echo "==> Removing $dst"
                    rm -rf -- "$dst"
                    ;;
                *)
                    echo "Refusing unsafe path: $dst" >&2
                    exit 1
                    ;;
            esac
            ;;
    
        direct)
            if [[ -z "$cmd" ]]; then
                echo "direct mode requires a command." >&2
                exit 2
            fi
    
            echo "==> Direct Linux task"
            echo "Working directory: $src"
            echo "Command: $cmd"
    
            cd "$src"
            exec "$mise" exec -- bash -c "$cmd"
            ;;
    
        build)
            if [[ -z "$cmd" ]]; then
                echo "build mode requires a command." >&2
                exit 2
            fi
    
            sync_project
    
            echo "==> Linux build task"
            echo "Working directory: $dst"
            echo "Command: $cmd"
    
            cd "$dst"
            exec "$mise" exec -- bash -c "$cmd"
            ;;
    esac
    EOF
    chmod 755 ~/.local/bin/linux-task
    bash -n ~/.local/bin/linux-task
    

    Windows 调度端

    使用 UTF-8 编码,保存为 D:\WSL\Scripts\linux-task.ps1 ,注意替换用户名和发行版:

    #Requires -Version 7.3
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet("direct", "build", "sync", "path", "clean")]
        [string]$Mode,
    
        [string]$Project = (Get-Location).Path,
    
        [string]$Command
    )
    
    $ErrorActionPreference = "Stop"
    # Keep embedded quotes intact even if the caller prefers Legacy passing.
    $PSNativeCommandArgumentPassing = "Standard"
    
    if (-not (Test-Path -LiteralPath $Project -PathType Container)) {
        throw "Project directory does not exist: $Project"
    }
    
    $Project = (Resolve-Path -LiteralPath $Project).Path
    
    if (($Mode -eq "direct" -or $Mode -eq "build") -and
        [string]::IsNullOrWhiteSpace($Command)) {
        throw "$Mode mode requires -Command"
    }
    
    # WSL enters the Windows directory itself and the Linux script resolves
    # ".", so the project path is never read back through the console.
    # --exec passes the arguments as-is, without another shell expansion.
    $WslArgs = @(
        "-d", "Debian",
        "-u", "dejavu",
        "--cd", $Project,
        "--exec", "/home/dejavu/.local/bin/linux-task",
        $Mode, "."
    )
    
    if (-not [string]::IsNullOrWhiteSpace($Command)) {
        $WslArgs += $Command
    }
    
    # Linux writes UTF-8. Decode it as UTF-8 so captured output such as
    # \`-Mode path\` keeps non-ASCII paths, then restore the caller's setting.
    $PreviousEncoding = [Console]::OutputEncoding
    [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
    try {
        & wsl.exe @WslArgs
        $ExitCode = $LASTEXITCODE
    }
    finally {
        [Console]::OutputEncoding = $PreviousEncoding
    }
    
    exit $ExitCode
    

    参数说明:

    • --exec :避免传入命令被外层 shell 二次转义展开;
    • --cd :由 WSL 内部定位目录,绕开中文或特殊路径的编码问题;
    • $PSNativeCommandArgumentPassing = "Standard" :PowerShell 7.3+ 的原生传参机制,保证嵌套引号原样传入 Linux;
    • 临时将控制台编码切为 UTF-8,防止 Linux 传回的中文日志乱码。

    调度器验证

    $ErrorActionPreference = 'Stop'
    $runner = (Get-Command linux-task.ps1).Source
    $root = Join-Path ([IO.Path]::GetTempPath()) ('Runner 验收-' + [guid]::NewGuid().ToString('N').Substring(0, 8))
    New-Item -ItemType Directory -Path $root | Out-Null
    Set-Content -LiteralPath (Join-Path $root 'hello.txt') -Value 'hello from Windows'
    Set-Content -LiteralPath (Join-Path $root '.git') -Value 'gitdir: not-a-real-repo'
    New-Item -ItemType Directory -Path (Join-Path $root 'node_modules') | Out-Null
    Set-Content -LiteralPath (Join-Path $root 'node_modules\win-only') -Value 'skip me'
    
    try {
        # build:在 ~/Build 下执行,看不到 .git 和 Windows 的 node_modules
        & $runner -Mode build -Project $root -Command '[[ "$PWD" == "$HOME/Build/"* ]] && test ! -e .git && test ! -e node_modules/win-only && grep -q "hello from Windows" hello.txt && echo built > only-in-linux.txt'
        if ($LASTEXITCODE -ne 0) { throw 'build 检查失败' }
        if (Test-Path -LiteralPath (Join-Path $root 'only-in-linux.txt')) { throw 'Linux 的输出写回了 Windows' }
    
        # direct:在 /mnt 下执行;变量和引号原样交给 Bash
        & $runner -Mode direct -Project $root -Command 'x=1; set -- "a b"; [[ "$PWD" == /mnt/* && -f .git && $x == 1 && $# == 1 ]]'
        if ($LASTEXITCODE -ne 0) { throw 'direct 检查失败' }
    
        # path:中文路径能正确读回
        $mirror = & $runner -Mode path -Project $root
        if ($mirror -notlike "*/$(Split-Path $root -Leaf)-*") { throw "镜像路径读回异常:$mirror" }
    
        # 退出码传回 Windows
        & $runner -Mode build -Project $root -Command 'exit 37'
        if ($LASTEXITCODE -ne 37) { throw '退出码没有传回 Windows' }
    
        Write-Host 'PASS'
    } finally {
        & $runner -Mode clean -Project $root | Out-Null
        Remove-Item -LiteralPath $root -Recurse -Force
    }
    

    配置 AI Agent 约束

    本地运行的 Codex 或 Claude Code 位于 Windows 宿主机。为了防止 Agent 误在 Windows 执行 Linux 编译命令,或进入 WSL 内部修改临时构建副本,需要在配置文件中明确约束。

    Git Bash 防止路径转换

    Claude Code 在 Windows 下默认通过 Git Bash 执行命令。Git Bash 会把 / 开头的参数改写为 Windows 盘符路径,导致传给 Linux 的参数被损坏。调用脚本时需要声明 MSYS_NO_PATHCONV=1 ,例如:

    MSYS_NO_PATHCONV=1 pwsh.exe -NoProfile \
        -File 'D:\WSL\Scripts\linux-task.ps1' \
        -Mode build -Project 'D:\Forgejo\App' -Command 'pnpm test'
    

    全局约束规则

    规则按作用域分两层,以 Codex 和 Claude Code 为例:

    • 机器全局
      • Codex:在 ~/.codex/config.toml 顶层写 developer_instructions ;
        • ~/.codex/AGENTS.md 规范测试与汇报流程(存在非空 AGENTS.override.md 时优先加载)。
        • Claude Code: ~/.claude/CLAUDE.md 做同样的分工约束,并声明 Git Bash 防路径转换。
    • 项目本地
      • 仓库根目录 AGENTS.md :记录项目独有的依赖命令与测试流水线。Claude Code 找不到 CLAUDE.md 时会自动递归读取;
        • 已有 CLAUDE.md 时用 @AGENTS.md 引入。

    agent-rules.webp

    参考下面两份英文规则模板,注意替换用户名、发行版和实际路径。

    developer_instructions

    This machine uses Windows as the canonical development host and Debian WSL2
    only as a Linux build/test runtime.
    
    Hard environment rules:
    - The canonical source tree and .git directory live on Windows.
    - Never treat /home/dejavu/Build as canonical source.
    - Never edit source in a WSL build mirror and expect those edits to be
      preserved.
    - Never perform Git-mutating operations from WSL. Git add, commit, checkout,
      switch, merge, rebase, reset, pull, and push must run with Windows Git
      against the canonical Windows repository.
    - Linux compilation, testing, package execution, Playwright, and Docker
      workloads should use the configured WSL runner:
      linux-task.ps1 -Mode build -Project <WindowsProjectPath> -Command '<LinuxCommand>'
    - Use direct mode only for lightweight Linux checks that do not create large
      dependency trees or build outputs.
    - Docker is provided by Docker Desktop through WSL integration. Never
      install or start a separate Docker Engine, docker.io, docker-ce, dockerd,
      or containerd daemon inside Debian.
    - Docker commands in Debian are expected to run as the dejavu user without
      sudo.
    - Windows and Linux toolchains must remain isolated. Do not invoke Windows
      Node, Python, Git, Rust, Go, or mise from WSL, and do not use Linux
      binaries as the Windows host toolchain.
    

    AGENTS.md

    由于帖子长度限制,此处开始被截断

    请查看原文:https://blog.dejavu.moe/posts/windows-wsl2-ai-dev-environment/#agentsmd

  • 收藏支持 1反对打赏

7 条回复

  • Misaka1
    UID 4124Lv.21 热心答主
    #1

    脸部特写强

  • 芒果
    UID 1010Lv.10 崭露头角超级版主楼主博主dejavu.moe
    #2

    @Showfom 论坛帖子似乎有长度限制?我好想编辑两次后面都被截断了笑哭

  • Showfom
    UID 1Lv.11 活跃成员管理员博主u.sb
    #3

    @芒果 #2 帖子内容长度有限制的

  • 芒果
    UID 1010Lv.10 崭露头角超级版主楼主博主dejavu.moe
    #4

    @Showfom #3 怪不得,我还以为出 bug 了日常合集32

  • Showfom
    UID 1Lv.11 活跃成员管理员博主u.sb
    #5

    我还是习惯直接在 Windows Terminal 里直接用 WSL2 宿主机只看看文件内容

  • 芒果
    UID 1010Lv.10 崭露头角超级版主楼主博主dejavu.moe
    #6

    @Showfom #5 感觉里外两套 Git、GPG、SSH 很麻烦,而且跨文件系统的 IO 还是烂的很,现在这样做,就纯把 WSL2 当 runner 了,还挺好的

  • Showfom
    UID 1Lv.11 活跃成员管理员博主u.sb
    亮了 1#7

    @芒果 #6 对呀,所以我就都在 WSL 里操作,不然跨平台了 I/O 贼垃圾

发表回复