zoukankan      html  css  js  c++  java
  • 使用xcodeproj 动态插入第三方代码

    # 为什么这么做?

      现在有这么一个使用场景,基线能生成项目A,项目B,项目C...如果只有项目A中使用SDK_A,其他项目都不使用,这时候就需要对基线进行差分,只有当我切换到项目A时,才插入SDK_A。

      不同于cocoapods的库管理方式,xcodeproj是通过脚本在编译前向项目中插入指定代码文件。

    # Xcode中的项目结构

      

       项目中都会有个主target作为根节点,底下有很多group节点,这些group节点管理着三类文件

        1. 常规的 .h/.m 文件

        2. 资源文件

        3. 库文件,分为静态库.a文件、动态库.framework文件、阉割版的动态库embed franework文件

    ## 如何引入

       这里涉及到xcodeproj的具体使用,贴一下官方文档:

    #获取项目
    $project = Xcodeproj::Project.open($project_path);
    
    #获取target,通常取第一个为项目的主target
    $target = $project.targets.first
    
    # 获取插件目录的group,如果不存在则创建
    $group_One = $project[$plugin_folder] || $project.main_group.find_subpath(File.join($plugin_folder), true);
    
    # 在目标目录新建group目录
    $group = $group_One.find_subpath($folderName, true)
    $SDK_PATH = $group_One.real_path.to_s + "/" + $plugin_folder + "/" + $folderName
    
    # 判断SDK_PATH目录下是否存在第三方代码,不存在则退出
    if !FileTest::exists?($SDK_PATH)
        puts "SDK file not found in #{$SDK_PATH}"
        exit 1
    end
    

      * project_path 是项目的路径

      * plugin_folder 是插入的目录名称,它是创建在项目目录底下的

      * folder_name 是插入的第三方库的文件夹的名称,它是创建在plugin_folder目录底下的

      

      注意,这里的第三库的代码文件其实和项目的代码是放在一块的,我们要做的只是将它和项目关联起来,也就是在XXX.xcodeproj文件中添加其引用。具体的关联就是建立target底下的group组后 添加上述3类文件的引用。

      1. .h/.m 文件添加引用

    if filePath.to_s.end_with?(".h") then
        fileReference = aGroup.new_reference(filePath);
        # aTarget.source_build_phase.add_file_reference(fileReference, true)
    elsif filePath.to_s.end_with?(".m", ".mm", ".cpp") then
        fileReference = aGroup.new_reference(filePath);
        aTarget.source_build_phase.add_file_reference(fileReference, true)
    

       需要注意的是.h文件只需要 在group底下new一个reference,.m文件需要将group底下的reference添加进source_build_phase

      2. 资源文件添加引用

    if filePath.to_s.end_with?(".bundle",".plist" ,".xml",".png",".xib",".js",".html",".css",".strings")
        fileReference = aGroup.new_reference(filePath);
        aTarget.resources_build_phase.add_file_reference(fileReference, true)
    

       根据资源文件创建其group的reference后,需要将其添加进resources_build_phase

      3. 库文件的引用

    if filePath.to_s.end_with?(".framework" ,".a")
        fileReference = aGroup.new_reference(filePath);
        build_phase = aTarget.frameworks_build_phase;
        build_phase.add_file_reference(fileReference);
        if $isEmbed == true
            #添加动态库
            $embed_framework.add_file_reference(fileReference)
            #勾上code sign on copy选项(默认是没勾上的)
            $embed_framework.files.each do |file|
                # puts "entry filePath : #{filePath} fileRef path : #{file.file_ref.path}"
                if filePath.end_with?(file.file_ref.path) then
                    if file.settings.nil? then
                        # puts "setting is nil"
                        file.settings = Hash.new
                    end
                    file.settings["ATTRIBUTES"] = ["CodeSignOnCopy", "RemoveHeadersOnCopy"]
                end
            end
        end
    

       库文件需将 reference 添加进frameworks_build_phase,如果是embed framework还需将其添加进embed_frameworks_build_phase,其在xcodeproj中的具体类型是PBXCopyFilesBuildPhase

    ## 如何清除

      因为SDK_A仅仅是项目A使用,如果从项目A切换到项目B,此时就得从XXX.xcodeproj文件中清除关于SDK_A的所有引用,其实是添加引用的一个逆向过程。

      上文中我们说到 将SDK_A插入{$PROJECT_PATH}/plugin_folder,所以只需遍历这个group,清除其中所有的reference即可。

    def removeBuildPhaseFilesRecursively(aTarget, aGroup)
      aGroup.files.each do |file|
          if file.real_path.to_s.end_with?(".m", ".mm", ".cpp") then
              aTarget.source_build_phase.remove_file_reference(file)
          elsif file.real_path.to_s.end_with?(".bundle",".plist" ,".xml",".png",".xib",".js",".html",".css",".strings") then
              aTarget.resources_build_phase.remove_file_reference(file)
          elsif file.real_path.to_s.end_with?(".framework" ,".a")
              aTarget.frameworks_build_phase.remove_file_reference(file)
              # remove embed ref
              if $isEmbed && !$embed_framework.nil?
                $embed_framework.remove_file_reference(file)
              end
          end
    
          # extra r+emove file ref
          file.remove_from_project
      end
      
      aGroup.groups.each do |group|
          # puts "group path : #{group.path}"
          if group.path == "embed"
              $isEmbed = true
          end
          removeBuildPhaseFilesRecursively(aTarget, group)
          $isEmbed = false
      end
    end
    

       * .m 文件的引用由source_build_phase移除

       * 资源文件的引用由resources_build_phase移除

       * 库文件的引用由frameworks_build_phase移除,其中embed的framework还需由embed_framework_build_phase额外移除一下

    # 后记

      xcodeproj 其实不光只是添加引用,xcode中的build_settings的所有选项几乎都可以通过xcodeproj的脚本控制,这里先按下不表。

      最后附一下 github的传送门地址:

      https://github.com/xuanyuelin/ManuallyInsertCodes

  • 相关阅读:
    【题解】P3796【模板】AC自动机(加强版)
    【模板】P3806点分治1
    【题解】P2602[JZOI2010]数字计数
    【题解】P2444 病毒
    【题解】[P3557 POI2013]GRA-Tower Defense Game
    【题解】DZY Loves Chinese
    【题解】[Ghd]
    【题解】CF264B Good Sequences
    【题解】P3162CQOI2012组装
    【题解】P2279消防局的设立
  • 原文地址:https://www.cnblogs.com/xiaoerheiwatu/p/15008998.html
Copyright © 2011-2022 走看看