You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle执行带空格参数的Xcode构建并通过xcpretty实时管道输出问题

Gradle执行Xcode构建并通过xcpretty实时输出的解决方案

方案一:基于Gradle exec块实现(推荐)

直接通过shell包装命令,既支持管道语法,也能正确处理带空格的参数,同时可实时输出构建日志:

// 可选:参数转义方法,避免变量包含特殊字符导致命令异常
def escapeShellArg(String arg) {
    return "'" + arg.replace("'", "'\\''") + "'"
}

exec {
    executable 'sh'
    args = [
        '-c',
        """xcodebuild archive \
        -project ${buildDir}/iPhone/Unity-iPhone.xcodeproj/ \
        -archivePath ${buildDir}/iPhone/Unity-iPhone.xcarchive \
        -sdk iphoneos \
        GCC_GENERATE_DEBUGGING_SYMBOLS=YES \
        DEBUG_INFORMATION_FORMAT=dwarf-with-dsym \
        DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT=NO \
        DWARF_DSYM_FOLDER_PATH=iOS_Dsym \
        DEBUGGING_SYMBOLS=YES \
        DEVELOPMENT_TEAM=RPGSNMH65P \
        CODE_SIGN_IDENTITY=${escapeShellArg(appleIdentity)} \
        CODE_SIGN_STYLE=Manual \
        USYM_UPLOAD_AUTH_TOKEN=${escapeShellArg(appcenter_login_token)} \
        PROVISIONING_PROFILE_SPECIFIER_APP=${escapeShellArg(provisioningProfile)} \
        -configuration Release \
        -scheme ${escapeShellArg(schemeName)} \
        | xcpretty
        """
    ]
    // 实时输出日志到控制台
    standardOutput = System.out
    errorOutput = System.err
    // 如果需要同时保存日志到文件,可将管道部分改为 | xcpretty | tee build_output.log
}

方案二:基于Groovy Process手动实现

如果不想通过shell包装,可手动构造两个进程建立管道,完全避免参数空格解析问题:

// 构造xcodebuild参数数组,数组元素会被当成独立参数,天然支持带空格的参数值
def xcodebuildArgs = [
    'xcodebuild',
    'archive',
    '-project',
    "${buildDir}/iPhone/Unity-iPhone.xcodeproj/",
    "-archivePath",
    "${buildDir}/iPhone/Unity-iPhone.xcarchive",
    "-sdk", "iphoneos",
    "GCC_GENERATE_DEBUGGING_SYMBOLS=YES",
    "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym",
    "DWARF_DSYM_FILE_SHOULD_ACCOMPANY_PRODUCT=NO",
    "DWARF_DSYM_FOLDER_PATH=iOS_Dsym",
    "DEBUGGING_SYMBOLS=YES",
    "DEVELOPMENT_TEAM=RPGSNMH65P",
    "CODE_SIGN_IDENTITY=${appleIdentity}",
    "CODE_SIGN_STYLE=Manual",
    "USYM_UPLOAD_AUTH_TOKEN=${appcenter_login_token}",
    "PROVISIONING_PROFILE_SPECIFIER_APP=${provisioningProfile}",
    "-configuration", "Release",
    "-scheme", "${schemeName}"
]

// 分别启动xcodebuild和xcpretty进程
def xcodebuildProc = xcodebuildArgs.execute()
def xcprettyProc = ['xcpretty'].execute()

// 建立管道:将xcodebuild的标准输出传给xcpretty的输入,错误输出直接打印到控制台
Thread.start { xcodebuildProc.errorStream.eachLine { println it } }
Thread.start {
    xcodebuildProc.inputStream.eachLine { line ->
        xcprettyProc.out.println(line)
        xcprettyProc.out.flush()
    }
    xcprettyProc.out.close()
}

// 实时打印xcpretty的输出
xcprettyProc.inputStream.eachLine { println it }
xcprettyProc.errorStream.eachLine { println it }

// 等待进程执行完成,检查返回码
xcodebuildProc.waitFor()
xcprettyProc.waitFor()

if (xcodebuildProc.exitValue() != 0) {
    throw new GradleException("Xcode构建失败,退出码:${xcodebuildProc.exitValue()}")
}

原代码异常原因说明

  • 直接将| xcpretty加入exec的args列表无效:|是shell特有的管道语法,Gradle exec默认直接启动可执行文件,不会经过shell解析,会被当成xcodebuild的普通参数导致命令报错。
  • 字符串形式的execute()方法会自动按空格拆分参数,即便手动加引号也会被错误解析,而数组形式的execute()虽然能正确处理带空格的参数,但本身不支持管道语法,需要手动处理进程间数据流。
  • 未配置输出流实时转发时,Gradle会缓存所有输出直到进程执行完成,才会一次性打印,导致无法实时查看进度。

内容的提问来源于stack exchange,提问作者ohthepain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:18:03