如何通过 CocoaPods post-install hook 修改 OTHER_LDFLAGS?

How can I modify OTHER_LDFLAGS via CocoaPods post-install hook?

我的项目使用 CocoaPods 和自定义 xcconfig 文件。到目前为止,这还没有造成任何问题:我只需要 #include 在我的自定义配置结束时使用 CocoaPods 生成的配置。

但是,我 运行 遇到了一个问题,需要根据 xcconfig 有条件地指定 OTHER_LDFLAGS,但我不知道该怎么做。

作为开始,我试过像这样简单地记录 OTHER_LDFLAGS,但实际上并没有记录标志:

post_install do |installer_representation|
  installer_representation.project.targets.each do |target|
    target.build_configurations.each do |config|      

      name = target.name
      puts "Target Found: #{name}"

      flags = config.build_settings['OTHER_LDFLAGS']
      puts "OTHER_LDFLAGS Found: #{flags}"
    end
  end
end

输出如下所示:

Target Found: Pods-ProjectName-DependencyName1
OTHER_LDFLAGS Found: # nothing here...?
Target Found: Pods-ProjectName-DependencyName2    
OTHER_LDFLAGS Found: # again nothing...
# etc...
Target Found: Pods-ProjectName  # Cool, this is the main target pod
OTHER_LDFLAGS Found: # ...

如何通过 CocoaPods post-install hook 实际修改 OTHER_LDFLAGS

我想不出修改 xcconfig 的好方法。我可以查看它们,甚至可以在 post 安装中更改它们,但我的更改不会写入 pod xcconfig.

这是我用来修改xcconfig文件的:

post_install do |installer|
    installer.libraries.each do |lib|
        if lib.name != "Pods-lib"
            next
        end
        target = lib.library
        target.xcconfigs.each do |key, value|
            config_file_path = target.xcconfig_path(key)
            File.open("config.tmp", "w") do |io|
                io << File.read(config_file_path).gsub(/-l"c\+\+"/, '').gsub(/-l"icucore"/,'')
            end

            FileUtils.mv("config.tmp", config_file_path)
        end
    end
end

直接修改post安装脚本中的OTHER_LD_FLAGS方法如下。但由于它们没有写入 xcconfig 文件,我不得不求助于上面的 hacky 解决方案。如果你能弄清楚如何将这些更改写入文件,那就太棒了。

post_install do |installer|
    libs_to_remove = ['c++', 'icucore']
    installer.libraries.each do |lib|
        if lib.name != "Pods-lib"
            next
        end
        target = lib.library
        target.xcconfigs.each do |key, value|
            other_ld = value.other_linker_flags
            libs = other_ld[:libraries]
            libs.subtract(libs_to_remove)
            value.other_linker_flags[:libraries] = libs
        end
    end
end

我遇到了同样的问题。首先,我尝试用显而易见的方式修改 OTHER_LDFLAGS

post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == "Pods-SomeTarget"
            puts "Updating #{target.name} OTHER_LDFLAGS"
            target.build_configurations.each do |config|
                config.build_settings['OTHER_LDFLAGS'] ||= ['$(inherited)']
                config.build_settings['OTHER_LDFLAGS'] << '-l"AFNetworking"'
            end
        end
    end
end

但是没用。相关的 xcconfig 没有得到改变。最终我找到了一个效果很好的解决方法——首先读取post_intall hook中的相关xcconfig文件内容,修改并写回:

v1.0

post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == "Pods-SomeTarget"
            puts "Updating #{target.name} OTHER_LDFLAGS"
            target.build_configurations.each do |config|
                xcconfig_path = config.base_configuration_reference.real_path
                xcconfig = File.read(xcconfig_path)
                new_xcconfig = xcconfig.sub('OTHER_LDFLAGS = $(inherited)', 'OTHER_LDFLAGS = $(inherited) -l"AFNetworking"')
                File.open(xcconfig_path, "w") { |file| file << new_xcconfig }
            end
        end
    end
end

编辑:对 v1.0 的改进。不是直接对xcconfigString内容进行操作,而是将xccconfig读入一个build_configurationHash,修改hash然后flush到xcconfig.

v1.5

post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == "Pods-SomeTarget"
            puts "Updating #{target.name} OTHER_LDFLAGS"
            target.build_configurations.each do |config|
                xcconfig_path = config.base_configuration_reference.real_path

                # read from xcconfig to build_settings dictionary
                build_settings = Hash[*File.read(xcconfig_path).lines.map{|x| x.split(/\s*=\s*/, 2)}.flatten]

                # modify OTHER_LDFLAGS
                build_settings['OTHER_LDFLAGS'] << '-l"AFNetworking"'

                # write build_settings dictionary to xcconfig
                build_settings.each do |key,value|
                  File.open(xcconfig_path, "a") {|file| file.puts key = value}
                end
            end
        end
    end
end

这是 v1.0 的用例: 我偶然发现了这个线程,因为我们有多个应用程序,它们都有单独的 xcconfigs 并共享公共 xcconfig 文件。一旦我们将应用程序扩展添加为目标并且无法再共享活动配置的项目级继承(如调试),使用 pods 就开始崩溃。 Sooooo 从上面使用 v1.0 你可以重命名 pod 级别的元素,比如 OTHER_LDFLAGS 到 PODS_OTHER_LDFLAGS 然后安全地将它们#include 到你的 xcconfigs 中(不踩踏其他值)将它们与常见的合并,应用程序,目标设置 ala:

OTHER_LDFLAGS = $(inherited) $(PODS_OTHER_LDFLAGS) $(COMMON_OTHER_LDFLAGS)

所以,在我的 pods 文件中,我们在 v1.0 的循环中有这样的部分:

    puts "Updating #{target.name} adapting settings like OTHER_LDFLAGS for merging at target level"
    xcconfig_path = config.base_configuration_reference.real_path
    xcconfig = File.read(xcconfig_path)
    xcconfig = xcconfig.sub('OTHER_LDFLAGS = $(inherited)', 'PODS_OTHER_LDFLAGS = ')
    xcconfig = xcconfig.sub('OTHER_CFLAGS = $(inherited)', 'PODS_OTHER_CFLAGS = ')
    xcconfig = xcconfig.sub('GCC_PREPROCESSOR_DEFINITIONS = $(inherited)', 'PODS_GCC_PREPROCESSOR_DEFINITIONS = ')
    xcconfig = xcconfig.sub('HEADER_SEARCH_PATHS = $(inherited)', 'PODS_HEADER_SEARCH_PATHS = ')
    xcconfig = xcconfig.sub('LIBRARY_SEARCH_PATHS = $(inherited)', 'PODS_LIBRARY_SEARCH_PATHS = ')
    File.open(xcconfig_path, "w") { |file| file << xcconfig }

和在目标级别 ala​​ 设置的胶水 xcconfig:

#include "../../Pods/Target Support Files/Pods-Fusion/Pods-Fusion.release.xcconfig"
#include "../../shared/main/config/release.xcconfig"
#include "../../shared/main/config/allTargetsCommon.xcconfig"
#include "Fusion.xcconfig"
#include "../../shared/main/config/merge.xcconfig"

各种 app/config/common/pod 设置被引入,merge.xcconfig 像这样将所有东西组合在一起:

//merge up the pods, common base target and target configs

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(PODS_GCC_PREPROCESSOR_DEFINITIONS) $(TARGET_GCC_PREPROCESSOR_DEFINITIONS) $(APP_GCC_PREPROCESSOR_DEFINITIONS)
HEADER_SEARCH_PATHS = $(inherited) $(PODS_HEADER_SEARCH_PATHS)
OTHER_CFLAGS = $(inherited) $(PODS_OTHER_CFLAGS) $(TARGET_OTHER_CFLAGS)
OTHER_LDFLAGS = $(inherited) $(PODS_OTHER_LDFLAGS) $(COMMON_OTHER_LDFLAGS)

根据上面的回答和 cocoapods and xcodeproj 的官方 ruby​​docs,我想出了这个解决方案,它完全基于上述 gems 提供的 API:

post_install do |installer|
    installer.aggregate_targets.each do |aggregate_target|
        aggregate_target.xcconfigs.each do |config_name, config_file|
            config_file.attributes['OTHER_LDFLAGS'] << '-l"AFNetworking"'

            xcconfig_path = aggregate_target.xcconfig_path(config_name)
            config_file.save_as(xcconfig_path)
        end
    end
end

这成功地将链接器标志 -l"AFNetworking" 添加到任何聚合目标 ('Pod-...') 的任何 xcconfig 文件。

在 Xcode8.3.3 和 Xcode9 Beta 4 上使用 cocoapods 1.2.0 和 1.3.0 进行了测试。

我是 运行 pod 版本 1.8.4,以上所有内容对我都不起作用,所以张贴在这里以防对任何人有帮助

以下脚本基于@mrvincenzo 的回答,但我必须做一些更改才能使其正常工作,这将成功地将 -ObjC 标志附加到 OTHER_LDFLAGS 请记住,这将保留现有列表并仅向其附加标志

post_install do |installer|
  installer.pods_project.targets.each do |target|
    #replace `Pod-target-lib` with your target library
    if target.name == "Pod-target-lib"
      puts "Updating #{target.name} OTHER_LDFLAGS"
      xcconfig_path = ""
      target.build_configurations.each do |config|
        new_xcconfig_path = config.base_configuration_reference.real_path
        if new_xcconfig_path != xcconfig_path
          xcconfig_path = config.base_configuration_reference.real_path

          # read from xcconfig to build_settings dictionary
          build_settings = Hash[*File.read(xcconfig_path).lines.map{|x| x.split(/\s*=\s*/, 2)}.flatten]

          # modify OTHER_LDFLAGS
          build_settings['OTHER_LDFLAGS'] = "-ObjC #{build_settings['OTHER_LDFLAGS']}"

          # clear current file content
          File.open(xcconfig_path, "w") {|file| file.puts ""}

          # write build_settings dictionary to xcconfig
          build_settings.each do |key,value|
            File.open(xcconfig_path, "a") {|file| file.puts "#{key} = #{value}"}
          end
        end
      end
    end
  end
end

如果您需要修改一些现有标志,此示例可能对您有用:

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            xcconfig_path = config.base_configuration_reference.real_path
            xcconfig = File.read(xcconfig_path)
            xcconfig_mod = xcconfig.gsub(/-framework "YourFramework"/, "-weak_framework \"YourFramework\"")
            File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
        end
    end
end

此解决方案基于来自@sven-driemecker的解决方案, 但它稍微好一点,因为它使用了更纯的椰子足 API。 它不会简单地附加字符串,而是将其添加到保持数据一致的集合中。

在当前示例中,我修改了聚合目标,例如 Pods-MYPROJECT 以将某些静态库排除在链接之外,因为它们已经由依赖项之一链接。

# Static librarties which are already linked with one of the frameworks
# They must not be linked with main executable
def static_libraries
    ['jre_emul_combined', 'jsr305', 'protobuf_runtime']
end

puts "Fix static libraries"

post_install do |installer|
    installer.aggregate_targets.each do |aggregate_target|
        aggregate_target.xcconfigs.each do |config_name, xcconfig|
            puts "Target name: #{aggregate_target.name}"

            puts "LD flags: #{xcconfig.other_linker_flags}"
        
            libraries = xcconfig.other_linker_flags[:libraries]
            puts "LD flags libraries: #{libraries}"
        
            modified_libraries = libraries.subtract(static_libraries)
            puts "LD flags libraries modified: #{modified_libraries}"
        
            xcconfig.other_linker_flags[:libraries] = modified_libraries
        
            xcconfig_path = aggregate_target.xcconfig_path(config_name)
            xcconfig.save_as(xcconfig_path)
        end
    end
end

如果您需要修改 LDFLAGS 并将一个框架移动到末尾,那么此脚本将帮助您。如果您有同一框架的两个版本并且您主要想使用特定的实现,这会有所帮助。 (由于 having FFMPEG twice in my app,我需要修复此问题,其中一个是 MobileVLCKit 的一部分。)使用 CocoaPods 1.9 和 1.10 进行测试。

post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == "Pods-MYPROJECT"
            puts "Updating #{target.name} OTHER_LDFLAGS"
            target.build_configurations.each do |config|
                xcconfig_path = config.base_configuration_reference.real_path

                # read from xcconfig to build_settings dictionary
                build_settings = Hash[*File.read(xcconfig_path).lines.map{|x| x.split(/\s*=\s*/, 2)}.flatten]

                # modify OTHER_LDFLAGS
                vlc_flag = ' -framework "MobileVLCKit"'
                build_settings['OTHER_LDFLAGS'].gsub!(vlc_flag, "")
                build_settings['OTHER_LDFLAGS'].gsub!("\n", "")
                build_settings['OTHER_LDFLAGS'] += vlc_flag + "\n"

                # write build_settings dictionary to xcconfig
                File.open(xcconfig_path, "w") do |file|
                  build_settings.each do |key,value|
                    file.write(key + " = " + value)
                  end
                end
            end
        end
    end
end