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

8

36

My project uses CocoaPods and also custom xcconfig files. Until now, this hasn't caused any problems: I've just had to #include the CocoaPods-generated configuration at the end of my custom configuration.

However, I've run into a problem where a need to conditionally specify OTHER_LDFLAGS based on the xcconfig, but I can't figure out how to do this.

As a start, I've tried simply logging the OTHER_LDFLAGS like this, but the flags aren't actually logged:

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

The output looks like this:

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: # ...

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

Melone answered 14/5, 2015 at 18:29 Comment(7)
What are you trying to do exactly ? If you want to change the OTHER_LDFLAGS, you can do it in the pod spec directlyChesterfield
After all I don't think this question makes sense. As @Chesterfield says: the pods can themselve declare the OTHER_LDFLAGS in their podspecs. You don't want to mess around with the pods target IMO.Pattypatulous
@Loegic, I'm in the unfortunate situation where I have to use a third-party static framework, which doesn't have a CocoaPod and is too big to work well as a pod (8 GB... :/). I've written scripts to pull this in, but it also requires additions to the OTHER_LDFLAGS. You can't override OTHER_LDFLAGS in your target's build settings, or else CocoaPods xcconfigs will be ignored. You can't do this in custom xcconfig, or one or the other config flags (depending on how you include pod xcconfig) will be overwritten. So, I thought to use install hook.Melone
@hfossli, you're assuming that if a project uses CocoaPods, it's the only way that dependencies will be included... unfortunately, this isn't the case here. Basically, I'm pulling in dependencies using both CocoaPods and custom shell scripts... is this a great idea? Nope. Unfortunately, I'm required to use a third-party framework that doesn't have a CocoaPod and is too big (8 GB). Again, not a good idea. I don't even want to try making this work with CocoaPods... manually changing OTHER_LDFLAGS in target build settings is easier, if I have to as a last resort.Melone
In our project we have a vustom xconfig for our app target. It is possible.Pattypatulous
Did you use $(inherited)?Pattypatulous
If your framework is outside of cocoapods i don't see why you change the pods target.Pattypatulous
M
44

I stumbled across the same problem. First I tried to modify OTHER_LDFLAGS with the obvious:

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

but it didn't work. The relevant xcconfig didn't get the change. Eventually I found a workaround that works well - first read the relevant xcconfig file content in the post_intall hook, modify it and write it back:

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

EDIT: Improvement over the v1.0. Instead of operating on xcconfig String content directly, read xccconfig into a build_configuration Hash, modify the hash and then flush it to 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
Meijer answered 4/8, 2015 at 8:39 Comment(6)
+1: This looks promising! :) I'm going to give this a go and make sure it works as intended. If so, I'll give you the win.Melone
The v1.5 worked awesome. The only change that I had to make was file = File.open(xcconfig_path, "w") (using write instead of append). Thanks!Melone
@Melone Glad it worked for you. Thanks for the bounty!Meijer
Thank you so much. I'm prefer v1.0 because its simple and works.Saidel
I wanted to remove OTHER_LDFLAGS from the pod xcconfig, so I used the v1.5 with the modification : File.open(xcconfig_path, "w") { |file| build_settings.each do |key,value| if key != 'OTHER_LDFLAGS' file.puts "#{key} = #{value}" end end } Works well !Kali
@mrvincenzo, With v1.5 I get the error odd number of arguments for Hash.Norby
G
17

Based on the answers above and the official rubydocs of cocoapods and xcodeproj, I came up with this solution, which is purely based on the APIs provided by the mentioned gems:

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

This successfully adds the linker flag -l"AFNetworking" to any xcconfig file of any aggregate target ('Pod-...').

Tested with cocoapods 1.2.0 and 1.3.0 on Xcode8.3.3 and Xcode9 Beta 4.

Gyrostat answered 3/8, 2017 at 11:56 Comment(2)
Worked like a charm. Thanks Sven.Troubadour
Works with cocoapods 1.10.1 on Xcode 12.3 (12C33). (I am using config.other_linker_flags[:simple] << '-ObjC' where :simple :frameworks, :weak_frameworks, :libraries, :force_load are appropriate symbols for the items in OTHER_LDFLAGS)Defrayal
O
3

Here's a use case for v1.0: I stumbled across this thread because we have multiple apps that all have individual xcconfigs and share common xcconfig files. Using pods started to fall apart once we added an app extension as a target and could no longer share the project level inheritance for the active config(like debug). Sooooo using v1.0 from above you can re-name the pod level elements, like OTHER_LDFLAGS to PODS_OTHER_LDFLAGS and then safely #include them into your xcconfigs (without stomping other values) merge them with common, app, target settings ala:

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

So, in my pods file we have a section like this inside a loop like 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 }

and a glue xcconfig that is set at the target level ala:

#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"

where the various app/config/common/pod settings are pulled in and merge.xcconfig pulls everything together like this:

//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)
Oberland answered 16/12, 2015 at 19:23 Comment(0)
U
2

If you need to modify the LDFLAGS and move one framework to the end then this script will help you. This helps if you have two versions of the same framework and you primarily want to use particular implementation. (I needed to fix this due to having FFMPEG twice in my app, one as part of MobileVLCKit.) Tested with CocoaPods 1.9 and 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
Ubiquitous answered 23/10, 2020 at 15:26 Comment(0)
C
1

In case you need to modify some existing flags this example might be useful for you:

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
Cp answered 29/7, 2020 at 11:55 Comment(0)
T
1

This solution is based on the one from @sven-driemecker, but it's slightly better since it utilizes even more pure cocoapods API. It won't simply append the string, but adds it to set which keeps data consistent.

In current example I modify aggregated targets, like Pods-MYPROJECT to exclude certain static libraries from linking since they are already linked by one of dependencies.

# 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
Twigg answered 30/9, 2020 at 8:33 Comment(0)
F
0

I am running pod version 1.8.4, and all the above didn't work for me, so posting this here incase it will help anyone

The below script is based on @mrvincenzo answer, but i had to do some changes to make it work, this will successfully append -ObjC flag to OTHER_LDFLAGS keep in mind this will keep the existing list and only append the flag to it

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
Fields answered 19/11, 2019 at 7:12 Comment(0)
E
-1

I couldn't figure out a good way to modify the xcconfig. I can view them and even change them in post install, but my changes don't get written to the pod xcconfig.

This is what I use to modify the xcconfig file:

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

The way to modify the OTHER_LD_FLAGS in the post install script directly is as follows. But since they don't get written to the xcconfig file, I had to resort to the hacky solution above. If you can figure out how to get these changes written to the file, it would be awesome.

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
Epps answered 21/6, 2015 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.