文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

iOS问题记录 - Xcode 14.3版本打包项目报错

2023-08-17 10:09

关注

前几天升级Xcode到14.3版本,运行项目报错,于是写了iOS问题记录 - Xcode 14.3版本运行项目报错这篇文章。没想到除了运行项目有问题,打包项目也有问题。

[Xcode菜单栏] -> [Product] -> [Archive],进行打包操作。执行到Run custom shell script '[CP] Embed Pods Frameworks'时报错,报错相关日志如下:

Symlinked...rsync --delete -av --filter P .*.?????? --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/xxx.framework" "/Users/xxx/Library/Developer/Xcode/DerivedData/app-dukdzczlzijlklamofogqicmtktj/Build/Intermediates.noindex/ArchiveIntermediates/app/InstallationBuildProductsLocation/Applications/app.app/Frameworks"building file list ... rsync: link_stat "xxx/../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/xxx.framework" failed: No such file or directory(2)rsync error: some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/97f6331a-ba75-11ed-a4bc-863efbbaf80d/Library/Caches/com.apple.xbs/Sources/rsync/rsync/main.c(996) [sender=2.6.9]Command PhaseScriptExecution failed with a nonzero exit code

从报错信息看,是因为文件或目录找不到报错。因为项目有改动,所以暂时不确定是不是Xcode 14.3版本的原因。找到一台装有14.2版本的电脑,拉取最新代码后执行打包操作,一切正常!那看来这锅Xcode得背,接下来就是找到具体原因和解决办法。

首先要确定这个错误是在执行什么代码的时候出现的,才能进一步分析。

找到Run custom shell script '[CP] Embed Pods Frameworks'的右侧按钮,展开详情:

screenshot1

可以看到执行的shell脚本路径是:

/Users/xxx/Library/Developer/Xcode/DerivedData/app-dukdzczlzijlklamofogqicmtktj/Build/Intermediates.noindex/ArchiveIntermediates/app/IntermediateBuildFilesPath/app.build/Release-iphoneos/app.build/Script-8D57CFCFEA49D25397FFD044.sh

shell脚本内容:

#!/bin/sh"${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh"

PODS_ROOT的值是什么呢?你可能会想,直接在shell脚本中加上一行echo "${PODS_ROOT}"不就知道了?不行的,每次执行打包操作都会重新生成这个shell脚本,改动不会生效。其实会执行这个自定义shell脚本,是因为在这有设置:

screenshot2

PODS_ROOT的定义在这:

screenshot3

验证这个很简单,只需要在这加上一行echo "${PODS_ROOT}"

screenshot4

重新执行打包操作,你会发现生成的shell脚本中也多了这一行:

#!/bin/shecho "${PODS_ROOT}""${PODS_ROOT}/Target Support Files/Pods-app/Pods-app-frameworks.sh"

同时在打包输出日志中也正常打印出PODS_ROOT的值。不知道有没有人和我有一样的疑问,生成的shell脚本中并没有导入其他shell脚本或定义PODS_ROOT,那么这个常量怎么来的?不难猜,PODS_ROOT应该来自环境变量。老办法,加上env命令打印一下环境变量:

screenshot5

重新执行打包操作,会打印一大堆环境变量,这里就不一一列出。看打印出来的环境变量,构建设置基本都在里面(没有一个个具体验证)。大致可以得出结论,在构建项目时,Xcode会把构建设置设为临时环境变量。

继续往下分析,找到Pods-app-frameworks.sh文件,根据报错相关日志,报错应该发生在install_framework函数中:

# Copies and strips a vendored frameworkinstall_framework(){  if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then    local source="${BUILT_PRODUCTS_DIR}/$1"  elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then    local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"  elif [ -r "$1" ]; then    local source="$1"  fi  local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"  if [ -L "${source}" ]; then    echo "Symlinked..."    source="$(readlink "${source}")"  fi  if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then    # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied    find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do      echo "Installing $f"      install_bcsymbolmap "$f" "$destination"      rm "$f"    done    rmdir "${source}/${BCSYMBOLMAP_DIR}"  fi  # Use filter instead of exclude so missing patterns don't throw errors.  echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"  local basename  basename="$(basename -s .framework "$1")"  binary="${destination}/${basename}.framework/${basename}"  if ! [ -r "$binary" ]; then    binary="${destination}/${basename}"  elif [ -L "${binary}" ]; then    echo "Destination binary is symlinked..."    dirname="$(dirname "${binary}")"    binary="${dirname}/$(readlink "${binary}")"  fi  # Strip invalid architectures so "fat" simulator / device frameworks work on device  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then    strip_invalid_archs "$binary"  fi  # Resign the code if required by the build settings to avoid unstable apps  code_sign_if_enabled "${destination}/$(basename "$1")"  # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.  if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then    local swift_runtime_libs    swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)    for lib in $swift_runtime_libs; do      echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""      rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"      code_sign_if_enabled "${destination}/${lib}"    done  fi}

执行rsync --delete...命令的时候,source变量的路径有问题。修改Pods-app-frameworks.sh文件,增加一些日志打印用于追踪source变量的变化。经测试,在执行source="$(readlink "${source}")"之前,source变量中的路径是绝对路径:

/Users/xxx/Library/Developer/Xcode/DerivedData/app-dukdzczlzijlklamofogqicmtktj/Build/Intermediates.noindex/ArchiveIntermediates/app/BuildProductsPath/Release-iphoneos/SDWebImage/SDWebImage.framework

执行后,变为相对路径:

../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/SDWebImage.framework

通过访达的前往文件夹功能,找到绝对路径所指向的位置:

screenshot6

SDWebImage.framework是一个替身(软链接/符号链接)。在SDWebImage目录路径下执行ls -l查看实际指向的路径:

lrwxr-xr-x  1 xxx  staff  85 Apr  7 20:23 SDWebImage.framework -> ../../../IntermediateBuildFilesPath/UninstalledProducts/iphoneos/SDWebImage.frameworkdrwxr-xr-x  3 xxx  staff  96 Apr  7 20:23 SDWebImage.framework.dSYM

看来readlink命令的作用就是获取软连接所指向的实际路径,那这路径为什么报错呢?有对比才能找到问题所在,同样的项目用Xcode 14.2版本执行打包操作,在SDWebImage目录路径下执行ls -l查看实际指向的路径:

lrwxr-xr-x  1 xxx  staff  212 Apr  7 20:30 SDWebImage.framework -> /Users/xxx/Library/Developer/Xcode/DerivedData/app-dukdzczlzijlklamofogqicmtktj/Build/Intermediates.noindex/ArchiveIntermediates/app/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/SDWebImage.frameworkdrwxr-xr-x  3 xxx  staff   96 Apr  7 20:30 SDWebImage.framework.dSYM

这么一对比,原因总算是找到了。Xcode 14.3版本构建时将软链接所指向的绝对路径改为了相对路径,导致找不到文件或目录。

那么该怎么修复呢?可以先看看readlink命令的文档,使用man(manual)命令查看:

man readlink

执行命令后得到的文档(省略部分):

NAME     stat, readlink – display file statusSYNOPSIS     stat [-FLnq] [-f format | -l | -r | -s | -x] [-t timefmt] [file ...]     readlink [-fn] [file ...]DESCRIPTION     The stat utility displays information about the file pointed to by file.  Read, write, or execute permissions of the named file are not required, but all directories listed in the pathname     leading to the file must be searchable.  If no argument is given, stat displays information about the file descriptor for standard input.     When invoked as readlink, only the target of the symbolic link is printed.  If the given argument is not a symbolic link and the -f option is not specified, readlink will print nothing and     exit with an error.  If the -f option is specified, the output is canonicalized by following every symlink in every component of the given path recursively.  readlink will resolve both     absolute and relative paths, and return the absolute pathname corresponding to file.  In this case, the argument does not need to be a symbolic link.     The information displayed is obtained by calling lstat(2) with the given argument and evaluating the returned structure.  The default format displays the st_dev, st_ino, st_mode, st_nlink,     st_uid, st_gid, st_rdev, st_size, st_atime, st_mtime, st_ctime, st_birthtime, st_blksize, st_blocks, and st_flags fields, in that order....

输入q退出文档查看。readlink命令参数不多,其中有个-f参数,这个参数的作用是递归找到第一个真实文件并返回该文件的绝对路径(个人理解)。举个例子🌰,假设A是真实文件,执行ln -s A B命令创建软链接B,执行ln -s B C命令创建软链接C,readlink C命令获取的是B,readlink -f C命令获取的是A的绝对路径。

修改install_framework函数中的readlink命令,加上-f参数。重新执行打包操作,打包成功!分析到这,问题似乎已经解决,可是当我执行完pod install命令后,Pods-app-frameworks.sh的文件内容又恢复原状了。这么看来,每次执行pod install命令都会重新生成Pods-app-frameworks.sh文件,那如果能找到生成文件的代码,在源头修改不就能解决吗?

不得不说macOS的可视化搜索真的不好用,电脑上的隐藏文件已经设置为显示,以install_framework为关键词搜索,搜不到有用的信息。没办法,只好用grep命令来搜索:

grep -R install_framework ~

-R表示递归搜索指定目录(~)下的全部文件,这里对用户目录进行搜索,如果你已经确定CocoaPods包所在目录,则可以指定更详细的目录路径加快搜索。搜索后,找到关键的文件路径:

/Users/xxx/.rvm/gems/ruby-3.0.0/gems/cocoapods-1.12.0/lib/cocoapods/generator/embed_frameworks_script.rb

embed_frameworks_script.rb文件内容:

require 'cocoapods/xcode'module Pod  module Generator    class EmbedFrameworksScript      # @return [Hash{String => Array}] Multiple lists of frameworks per      #         configuration.      #      attr_reader :frameworks_by_config      # @return [Hash{String => Array}] Multiple lists of frameworks per      #         configuration.      #      attr_reader :xcframeworks_by_config      # @param  [Hash{String => Array] frameworks_by_config      #         @see #frameworks_by_config      #      # @param  [Hash{String => Array] xcframeworks_by_config      #         @see #xcframeworks_by_config      #      def initialize(frameworks_by_config, xcframeworks_by_config)        @frameworks_by_config = frameworks_by_config        @xcframeworks_by_config = xcframeworks_by_config      end      # Saves the resource script to the given pathname.      #      # @param  [Pathname] pathname      #         The path where the embed frameworks script should be saved.      #      # @return [void]      #      def save_as(pathname)        pathname.open('w') do |file|          file.puts(script)        end        File.chmod(0755, pathname.to_s)      end      # @return [String] The contents of the embed frameworks script.      #      def generate        script      end      private      # @!group Private Helpers      # @return [String] The contents of the embed frameworks script.      #      def script        script = <<-SH.strip_heredoc#{Pod::Generator::ScriptPhaseConstants::DEFAULT_SCRIPT_PHASE_HEADER}if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then  # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy  # frameworks to, so exit 0 (signalling the script phase was successful).  exit 0fiecho "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"BCSYMBOLMAP_DIR="BCSymbolMaps"#{Pod::Generator::ScriptPhaseConstants::RSYNC_PROTECT_TMP_FILES}# Copies and strips a vendored frameworkinstall_framework(){  if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then    local source="${BUILT_PRODUCTS_DIR}/$1"  elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then    local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"  elif [ -r "$1" ]; then    local source="$1"  fi  local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"  if [ -L "${source}" ]; then    echo "Symlinked..."    source="$(readlink "${source}")"  fi  if [ -d "${source}/${BCSYMBOLMAP_DIR}" ]; then    # Locate and install any .bcsymbolmaps if present, and remove them from the .framework before the framework is copied    find "${source}/${BCSYMBOLMAP_DIR}" -name "*.bcsymbolmap"|while read f; do      echo "Installing $f"      install_bcsymbolmap "$f" "$destination"      rm "$f"    done    rmdir "${source}/${BCSYMBOLMAP_DIR}"  fi  # Use filter instead of exclude so missing patterns don't throw errors.  echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \\"- CVS/\\" --filter \\"- .svn/\\" --filter \\"- .git/\\" --filter \\"- .hg/\\" --filter \\"- Headers\\" --filter \\"- PrivateHeaders\\" --filter \\"- Modules\\" \\"${source}\\" \\"${destination}\\""  rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"  local basename  basename="$(basename -s .framework "$1")"  binary="${destination}/${basename}.framework/${basename}"  if ! [ -r "$binary" ]; then    binary="${destination}/${basename}"  elif [ -L "${binary}" ]; then    echo "Destination binary is symlinked..."    dirname="$(dirname "${binary}")"    binary="${dirname}/$(readlink "${binary}")"  fi  # Strip invalid architectures so "fat" simulator / device frameworks work on device  if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then    strip_invalid_archs "$binary"  fi  # Resign the code if required by the build settings to avoid unstable apps  code_sign_if_enabled "${destination}/$(basename "$1")"  # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.  if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then    local swift_runtime_libs    swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\\\/\\(.+dylib\\).*/\\\\1/g | uniq -u)    for lib in $swift_runtime_libs; do      echo "rsync -auv \\"${SWIFT_STDLIB_PATH}/${lib}\\" \\"${destination}\\""      rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"      code_sign_if_enabled "${destination}/${lib}"    done  fi}#{Pod::Generator::ScriptPhaseConstants::INSTALL_DSYM_METHOD}#{Pod::Generator::ScriptPhaseConstants::STRIP_INVALID_ARCHITECTURES_METHOD}#{Pod::Generator::ScriptPhaseConstants::INSTALL_BCSYMBOLMAP_METHOD}# Signs a framework with the provided identitycode_sign_if_enabled() {  if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then    # Use the current code_sign_identity    echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"    local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"    if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then      code_sign_cmd="$code_sign_cmd &"    fi    echo "$code_sign_cmd"    eval "$code_sign_cmd"  fi}        SH        contents_by_config = Hash.new do |hash, key|          hash[key] = ''        end        frameworks_by_config.each do |config, frameworks|          frameworks.each do |framework|            contents_by_config[config] << %(  install_framework "#{framework.source_path}"\n)          end        end        xcframeworks_by_config.each do |config, xcframeworks|          xcframeworks.select { |xcf| xcf.build_type.dynamic_framework? }.each do |xcframework|            target_name = xcframework.target_name            name = xcframework.name            contents_by_config[config] << %(  install_framework "#{Target::BuildSettings::XCFRAMEWORKS_BUILD_DIR_VARIABLE}/#{target_name}/#{name}.framework"\n)          end        end        script << "\n" unless contents_by_config.empty?        contents_by_config.keys.sort.each do |config|          contents = contents_by_config[config]          next if contents.empty?          script << %(if [[ "$CONFIGURATION" == "#{config}" ]]; then\n)          script << contents          script << "fi\n"        end        script << <<-SH.strip_heredoc        if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then          wait        fi        SH        script      end      # @param  [Xcode::FrameworkPaths] framework_path      #         the framework path containing the dSYM      #      # @return [String, Nil] the name of the dSYM binary, if found      #      def dsym_binary_name(framework_path)        return nil if framework_path.dsym_path.nil?        if (path = Pathname.glob(framework_path.dsym_path.join('Contents/Resources/DWARF', '**/*')).first)          File.basename(path)        end      end    end  endend

关键代码是script方法,方法内部可以分为两部分:

if [[ "$CONFIGURATION" == "Debug" ]]; then  install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"fiif [[ "$CONFIGURATION" == "Release" ]]; then  install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"fiif [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then  waitfi

以上只是简单了解一下Pods-app-frameworks.sh文件内容是怎么生成的,如果你对这感兴趣,可以尝试自己调试CocoaPods源码,调试环境的搭建可以参考CocoaPods - 源码调试环境搭建

embed_frameworks_script.rb文件中的source="$(readlink "${source}")"改为source="$(readlink -f "${source}")",然后执行pod install命令重新生成Pods-app-frameworks.sh文件,接着重新打包,一切正常!

不过,这也不是长久之计,关键还是要CocoaPods修复这个问题。已经有人提了issue,连PR都有了,只不过还不知道啥时候发新版本。逛issue的过程中,发现了不同于前面的解决办法:

  1. 切换Command Line Tools版本

Xcode 14.2版本有7.15GB,重新下载有点费时间,所以先看看只下载Command Line Tools for Xcode 14.2(671MB)行不行,安装完成后发现不行,还是得下载Xcode 14.2版本。在Xcode 14.3版本中设置Command Line Tools版本为14.2,尝试打包还是报错。看来这方法不太行,而且如果都安装有14.2版本,那直接用不就好了。

  1. 利用Hook修改Pods-app-frameworks.sh文件内容

前面的解决方法是在生成文件的时候加上-f参数,而现在这个方法是在已经生成的文件上修改。

screenshot7

这想法很好,不过直接拿来用会有问题。一是替换内容的时候双引号没有转义,二是存在多target的时候会找不到文件。除了解决这两个问题,再简单优化一下,不再需要手动设置项目名称:

post_install do |installer|  installer.pods_project.targets.each do |target|    shell_script_path = "Pods/Target Support Files/#{target.name}/#{target.name}-frameworks.sh"    if File::exists?(shell_script_path)      shell_script_input_lines = File.readlines(shell_script_path)      shell_script_output_lines = shell_script_input_lines.map { |line| line.sub("source=\"$(readlink \"${source}\")\"", "source=\"$(readlink -f \"${source}\")\"") }      File.open(shell_script_path, 'w') do |f|        shell_script_output_lines.each do |line|          f.write line        end      end    end  endend

如果没有看前面的问题分析,建议先看一下。解决问题的方法有很多,以下罗列一些,供大家随意选择。

2023/04/10更新:如果你的项目是Flutter项目,除了以下方法,还可以通过升级Flutter到3.7.10或更高版本的方式解决该问题。

  1. 升级CocoaPods版本

个人比较推荐的方法,但是可能暂时还无法使用。问题将在1.12.1版本修复,如果你遇到这个问题时,CocoaPods版本已经发布到1.12.1或更高版本,推荐通过升级到最新版本解决该问题。

  1. 修改Podfile文件

加上这段代码:

post_install do |installer|  installer.pods_project.targets.each do |target|    shell_script_path = "Pods/Target Support Files/#{target.name}/#{target.name}-frameworks.sh"    if File::exists?(shell_script_path)      shell_script_input_lines = File.readlines(shell_script_path)      shell_script_output_lines = shell_script_input_lines.map { |line| line.sub("source=\"$(readlink \"${source}\")\"", "source=\"$(readlink -f \"${source}\")\"") }      File.open(shell_script_path, 'w') do |f|        shell_script_output_lines.each do |line|          f.write line        end      end    end  endend

重新执行pod install命令解决问题。你可能会遇到以下报错:

undefined method `exists?' for File:Class

从Ruby 3.2.0版本开始,exists?方法被移除了,解决方法是替换为exist?方法。参考文档:Ruby 3.2.0 Released

  1. 修改embed_frameworks_script.rb文件

文件位于CocoaPods包下的lib/cocoapods/generator/embed_frameworks_script.rb路径,将文件中的source="$(readlink "${source}")"替换为source="$(readlink -f "${source}")",重新执行pod install命令解决问题。

  1. 使用Xcode 14.2版本

既然都升级了,个人不是很推荐退回低版本,如果确实有需要,Xcode历史版本官方下载(需要登录)。

如果这篇文章对你有所帮助,请不要吝啬你的点赞👍加星🌟,谢谢~

来源地址:https://blog.csdn.net/crasowas/article/details/129974391

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯