35

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?

JRG-Developer
  • 12,454
  • 8
  • 55
  • 81
  • 1
    What are you trying to do exactly ? If you want to change the OTHER_LDFLAGS, you can do it in the pod spec directly – Loegic Jul 28 '15 at 10:07
  • After all I don't think this question makes sense. As @Loegic says: the pods can themselve declare the OTHER_LDFLAGS in their podspecs. You don't want to mess around with the pods target IMO. – hfossli Aug 05 '15 at 04:52
  • @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. – JRG-Developer Aug 05 '15 at 17:24
  • @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. – JRG-Developer Aug 05 '15 at 17:31
  • In our project we have a vustom xconfig for our app target. It is possible. – hfossli Aug 06 '15 at 01:00
  • Did you use $(inherited)? – hfossli Aug 06 '15 at 01:02
  • If your framework is outside of cocoapods i don't see why you change the pods target. – hfossli Aug 06 '15 at 01:04

8 Answers8

43

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
Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
tonymontana
  • 5,728
  • 4
  • 34
  • 53
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.

Sven Driemecker
  • 3,421
  • 1
  • 20
  • 22
  • Worked like a charm. Thanks Sven. – Ben Thomas May 07 '19 at 03:17
  • 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) – Kanstantsin Bucha Jan 26 '21 at 10:48
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)
troppoli
  • 558
  • 6
  • 13
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
mkj_is
  • 21
  • 1
  • 1
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
Nikita Ivaniushchenko
  • 1,425
  • 11
  • 11
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
Amr Labib
  • 3,995
  • 3
  • 19
  • 31
0

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
Leszek Szary
  • 9,763
  • 4
  • 55
  • 62
-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
Kostub Deshmukh
  • 2,852
  • 2
  • 25
  • 35