New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tweaks doesn't link when used with CocoaPods and user defined build configuration #1934

Closed
mokagio opened this Issue Mar 28, 2014 · 31 comments

Comments

Projects
None yet
8 participants
@mokagio
Copy link

mokagio commented Mar 28, 2014

Premise

If I add Tweaks manually instead than with CocoaPods it links, that's why I'm opening the issue on this repo.

Issue

I experienced an unexpected behaviour using Tweaks through CocoaPods in a project with user defined build configurations:

Given a project using Tweaks through CocoaPods, with a user defined build configuration, and calling FBTweakInline at least once.

When building for the user defined build configuration.

Expected result the build process succeeds.

Actual result the build process fails during linking.

Digging into it

When building on a custom build configuration, meaning not the stock Debug and Release, the process fails during the linking, with this error:

Undefined symbols for architecture i386:
  "__FBTweakIdentifier", referenced from: ...
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I noticed that between FBTweakEnabled.h, FBTweakInline.m, and FBTweakInlineInternal.h they use #ifs and #ifdefs based on DEBUG and FB_TWEAK_ENABLED to enable Tweaks. This made me think about the Preprocessors Macros.

I then went to check the Pods target settings and noticed that despite my custom build configuration was made by duplicating Debug, the DEBUG macro wasn't set. This is an issue already addressed by you guys.

So I added this to my Podfile:

xcodeproj "NAME.xcodeproj", { 'Beta' => :debug }

Now the project builds, but only when modelling from Debug.

If I manually, or through the post_install hook, add FB_TWEAK_ENABLED=1 to my custom build configuration it builds. Even if I skip the xcodeproj set step.

But then if I set FB_TWEAK_ENABLED=0 it doesn't build anymore.

I made this demo repo to reproduce the issue.

This build configurations tweaking is something I've never done before, so I apologize if I'm missing something basic.

@mokagio mokagio changed the title Tweaks doesn't link when using with CocoaPods and user defined build configuration Tweaks doesn't link when used with CocoaPods and user defined build configuration Mar 28, 2014

@fabiopelosin

This comment has been minimized.

Copy link
Member

fabiopelosin commented Mar 28, 2014

🍻

Now the project builds, but only when modelling from Debug.

Can you clarify this sentence?

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Mar 28, 2014

🍻 to you!

From the xcodeproj reference:

It is possible also to specify whether the build settings of your custom build configurations should be modeled after the release or the debug presets.

Adding this to the Podfile makes it build with the Beta configuration:

xcodeproj "NAME.xcodeproj", { 'Beta' => :debug }

But this instead doesn't:

xcodeproj "NAME.xcodeproj", { 'Beta' => :release }

See this Podfile and this post_install in my demo project for better example.

@fabiopelosin

This comment has been minimized.

Copy link
Member

fabiopelosin commented Mar 28, 2014

But is done by design on Tweaks, no? To my understanding they fail the linking in non debug build configurations on purpose to avoid the accidental inclusion of the library in release builds (as it would be rejected by the App store).

Moreover currently CocoaPods doesn't support the specification of the deps per build configuration so to make a release build the library needs to be commented in the Podfile and the a new installation should be performed.

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Mar 28, 2014

Nope ☝️, from the README:

In release builds, the FBTweakValue macro expands to just the default value, so there's no performance impact.

The behaviour of Tweaks is defined by the FB_TWEAK_ENABLED macro, which is defined here if not defined already: FBTweakEnabled.h.

The thing is that if I set it, either manually or through the post_install hook, to 0, FB_TWEAK_ENABLED=0 the build process crashes.

And this doesn't happen in release, or if I do it with a drag-n-dropped version of Tweaks.

I'll come back later with a better explanation of how they use the macro. But it can be reproduced here.

Cheers

@neonichu

This comment has been minimized.

Copy link
Member

neonichu commented Mar 29, 2014

Maybe I am doing something wrong, but I cannot reproduce the issue with your example project.
I changed the post_install.rb you provide so that it looks like this:

# Linking still fails with these values
#
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'DEBUG=0']
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FB_TWEAK_ENABLED=0']

# Linking succeeds with these values
# 
# config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'DEBUG=1']
# config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FB_TWEAK_ENABLED=1']

With that, both the -Beta and the normal scheme build correctly for me. Please clarify how to use the example to reproduce the issue you are talking about.

@mokagio mokagio assigned CocoaPodsBot and unassigned CocoaPodsBot Mar 29, 2014

@CocoaPodsBot

This comment has been minimized.

Copy link

CocoaPodsBot commented Mar 30, 2014

Issue has been confirmed by @neonichu

@mokagio mokagio assigned CocoaPodsBot and unassigned CocoaPodsBot Mar 30, 2014

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Mar 31, 2014

Hey @neonichu sorry for replying so late... I saw that you confirmed the issue so probably you figured out how to reproduce it already, anyway I pushed a number of Podfiles to my example repo , just swap one Podfile.not_building_X with the default one and pod install to reproduce.

I also updated to CocoaPods 0.30.0, but it still happens.

@neonichu

This comment has been minimized.

Copy link
Member

neonichu commented Mar 31, 2014

Nah, actually, we confirmed everything we looked at yesterday, because we were going through all open issues and evaluating them.

But I can reproduce it just fine now by using one of the Podfile.not_building_X files and building the -Beta scheme. Thanks.

@fabiopelosin fabiopelosin added the Defect label Mar 31, 2014

@grp

This comment has been minimized.

Copy link

grp commented Apr 2, 2014

Hey all, sorry about this. I just saw this issue now, going to investigate what's going wrong.

@grp

This comment has been minimized.

Copy link

grp commented Apr 2, 2014

Interesting, so I tried adding some #warnings to debug what the macros were being set to. For the project that won't built, here's what I saw:

In file included from /Users/grp/code/TweaksBuildConfigurationsDemo/Pods/Tweaks/FBTweak/FBTweakInline.m:10:
[..more imports..]
/Users/grp/code/TweaksBuildConfigurationsDemo/Pods/Tweaks/FBTweak/FBTweakEnabled.h:15:2: warning: DEBUG=0 [-W#warnings]
#warning DEBUG=0
 ^
1 warning generated.
In file included from /Users/grp/code/TweaksBuildConfigurationsDemo/TweaksBuildConfigurationsDemo/AppDelegate.m:11:
[..more imports..]
/Users/grp/code/TweaksBuildConfigurationsDemo/Pods/Headers/Tweaks/FBTweakEnabled.h:13:2: warning: DEBUG=1 [-W#warnings]
#warning DEBUG=1
 ^
1 warning generated.

It looks like the issue is that the DEBUG macro is different when building the Pods versus when building the app itself. I assume the same happens with the FB_TWEAK_ENABLED macro if you set it manually.

I don't think this I'm any further towards fixing this than @mokagio was, though...

@grp

This comment has been minimized.

Copy link

grp commented Apr 2, 2014

Strangely, the target where DEBUG=1 is the app itself, despite being created through 'Beta' => :release (which I would think would be patterned off a release configuration would have DEBUG=0).

Basing the target off of :debug instead, so it looks like the issue could be how preprocessor flags are configured for configurations created in Cocoapods?

@fabiopelosin

This comment has been minimized.

Copy link
Member

fabiopelosin commented Apr 2, 2014

@grpaul great investigative work!

Strangely, the target where DEBUG=1 is the app itself, despite being created through 'Beta' => :release (which I would think would be patterned off a release configuration would have DEBUG=0).

CocoaPods never creates a build configuration in the App, that syntax is reserved to hint the Pods project. So, if I'm reading your comment correctly I see two alternatives:

  • The Beta build configuration in the app was duplicated from the release one by the user
  • The xcconfig set by CocoaPods in the target for this build configuration is specifying DEBUG=1

Could you investigate what is configuring this build setting in the App, via the Xcode levels features of the build configuration view of the target?

I don't think this I'm any further towards fixing this than @mokagio was, though...

Nonetheless we discovered that the issue with Tweaks (or at least one) appears to be related to a mismatch of the DEBUG flag – Which I would call a lot of progress towards fixing it 😄 –.

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 2, 2014

If I understand it correctly (sorry haven’t tried the sample project yet) the problem is that we never even define the DEBUG CPP macro in release configurations: https://github.com/CocoaPods/Xcodeproj/blob/master/lib/xcodeproj/constants.rb.

In short, I think we need to set the default in Xcodeproj to be DEBUG=0 for release configurations.

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 2, 2014

Looking at a newly created project by Xcode.app, it doesn’t set DEBUG=0 either for the release config. Which makes sense, as most use #ifdef DEBUG and setting it to 0 would make that guard pass, even though it’s actually turned off.

So in retrospect, we already do the right thing and Tweaks should treat the macro being undefined the same as it being set to 0.

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Apr 2, 2014

@grpaul thanks for looking at this.

@irrationalfab:

The Beta build configuration in the app was duplicated from the release one by the user

Nope, the Beta build was made by duplicating Debug

Could you investigate what is configuring this build setting in the App, via the Xcode levels features of the build configuration view of the target?

This is how the preprocessors macro view by levels, it stays the same despite what symbol I use in Beta => in the Podfile:

screen shot 2014-04-02 at 13 28 50

For what is worth I agree with @alloy in regard of not setting the DEBUG value if the custom build configuration is set to :release in the Podfile, it's consistent with Xcode

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Apr 2, 2014

This whole 'Beta' => :release hit me as a semantically wrong and I got the doubt of having made some conceptual mistake.

So I created a new build configuration, QA, by duplicating Release. The issue still stands.

With this Podfile:

platform :ios, "6.0"

xcodeproj "TweaksBuildConfigurationsDemo.xcodeproj", { 'Beta' => :debug, 'QA' => :release }

target "TweaksBuildConfigurationsDemo" do
  pod 'Tweaks', '1.0.0'
end

target "TweaksBuildConfigurationsDemoTests" do

end

Now Beta always builds, while Release behave like this:

  • No macro set: builds
  • FB_TWEAK_ENABLED=0: builds
  • FB_TWEAK_ENABLED=1: doesn't build

I set the macro at project level:

screen shot 2014-04-02 at 13 53 52

@grp

This comment has been minimized.

Copy link

grp commented Apr 9, 2014

@alloy In my testing, I was seeing the DEBUG macro set to 1 in the release configurations, not unset. (Tweaks treats unset the same as zero, as you suggest.) But that's only in the app target — when building the same configuration for the pods themselves, DEBUG is (correctly) 0.

@grp

This comment has been minimized.

Copy link

grp commented Apr 9, 2014

I used this code to print out what the value of DEBUG is when building the various projects:

#if defined(DEBUG) && DEBUG
#warning DEBUG=1
#else
#warning DEBUG=0
#endif

The results from Xcode looked like this. Note how DEBUG=1 in the application, but DEBUG=0 in the Pods project's targets:
screenshot 2014-04-09 12 28 47

I'm not sure what's setting DEBUG=1 there, but I think that's incorrect: when building a :release-derived configuration, DEBUG should bet set to 0 for the application target too.

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 10, 2014

@grpaul Can you please share that test project?

@grp

This comment has been minimized.

Copy link

grp commented Apr 10, 2014

@alloy I'm using @mokagio 's https://github.com/mokagio/TweaksBuildConfigurationsDemo. I copied Podfile.not_building_4 over Podfile, ran Cocoapods, then opened in Xcode. (The only code change was to paste the code above into FBTweakEnabled.h.)

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 10, 2014

@grpaul Ugh, completely stepped over that, my bad.

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 10, 2014

@mokagio Ok tell me if I’m missing something here. As I understand it, basically you are wondering why you can’t simply change 'Beta' => :debug to 'Beta' => :release in your Podfile. Is that correct?

When you change it in your Podfile only, CocoaPods does the right thing and leaves DEBUG out
of the CPP flags completely. However, in your own Xcode project, your Beta scheme is still using the Beta configuration which still sets DEBUG=1. If you remove DEBUG=1 from your Xcode project’s Beta configuration (at the project level), then the project builds again.

The reason why it’s important that this matches in both the Pods project and your own project is because the FBTweak headers are read and interpreted by both the projects when you build. Now when the Pods project is built without DEBUG set, the FBTweak code doesn’t actually get compiled and assembled into libPods.a, but since DEBUG=1 is set in your project the FBTweak headers think the code will be in the product and it references symbols that are not actually in any of the built object files.

The reason this works in the manual case is because the headers are only ever read once and the same CPP flags set in the Beta configuration of your project apply to all sources being built.

The conclusion is that atm you need to perform slightly more work when using CocoaPods and creating more configurations and there are differences in the CPP flags they set.

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 10, 2014

@irrationalfab The only real solution I can come up with for this is that we derive the full configuration from the user’s project at pod install time. For this to work I will have to finish my xcconfig parser and full setting evaluation (across base, project, and target levels), so that we can query the user’s project for all settings for a particular configuration at the (cascaded) target level.

@AliSoftware

This comment has been minimized.

Copy link
Contributor

AliSoftware commented Apr 10, 2014

@alloy not sure I followed all the present issue correctly but isn't this kind of issue one that would be solved by relying on Xcode's "Copy Files" phase to let it copy the headers properly, like discussed in CocoaPods/Core#81 (comment)? Or maybe I am totally misleading? (It's possible, I'm not sure of seeing all the consequences there and it has been a pretty tiring week ^^)

Actually we also had a similar issue in some of our projects regarding pods like "MagicalRecord", which conditionally compile some code depending on if MR_SHORTHAND Is #define'd, so we had to make sure this definition was present in both the pod and the app project before #import-ing the header to be sure the code declaredin the header used in the app matches the methods compiled in the pod lib

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 11, 2014

@AliSoftware Nope. The location of the headers doesn’t matter, it will still be read and evaluated by all targets that need the header and it will still be evaluated with different configuration settings.

The important thing to remember here is that you cannot conditionally compile stuff by setting CPP flags from your app target only.

@AliSoftware

This comment has been minimized.

Copy link
Contributor

AliSoftware commented Apr 11, 2014

Ok got it, after a night of good sleep I now realize it too 😉

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 13, 2014

Alas, it’s a hard to understand issue with Xcode’s opaque nature :(

mokagio added a commit to mokagio/TweaksBuildConfigurationsDemo that referenced this issue Apr 16, 2014

Added post_install hook to configure the Pods properly.
Pods project and app project have to be inline, otherwise the Tweaks headers will be compiled in a way that is different from the one that the project expects. This comment by @alloy made the situation more clear: CocoaPods/CocoaPods#1934 (comment)
@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Apr 16, 2014

@alloy sorry for this super late reply, and thanks for the explanation, it's very clear 🙏

I understand that the configuration value set in the xcodeproj has to match the one of the actual project. In fact, as I wrote, to avoid confusion I created a QA build configuration by duplicating Release, and setting it as 'QA' => :release in the Podfile.

The intention of the QA configuration was to have an setup that is the same as Release, but with the Tweaks enabled. One here could argue that the setup is not the same as Release anymore then, but let's go over it for the sake of this example.

The linking failure was still happening when setting FB_TWEAK_ENABLED=1 😱

This line enlightened me:

The reason why it’s important that this matches in both the Pods project and your own project is because the FBTweak headers are read and interpreted by both the projects when you build

FB_TWEAK_ENABLED=1 was set only at project level! Adding this post_install hook to add the macro to the Pods as well fixed the issue.

Thanks 🍻

@mokagio

This comment has been minimized.

Copy link
Author

mokagio commented Apr 16, 2014

Should the issue be closed now?

@alloy

This comment has been minimized.

Copy link
Member

alloy commented Apr 17, 2014

No problem at all, I’m glad it’s at least clear now :)

I’ll close it indeed.

@alloy alloy closed this Apr 17, 2014

@yifeic

This comment has been minimized.

Copy link

yifeic commented Sep 30, 2015

Thanks @mokagio for the post_install hook.
I tweaked the following 2 lines to work with CocoaPods v0.38.2.

installer_representation.project.targets.each do |target|
    if target.name == "Pods-TweaksBuildConfigurationsDemo-Tweaks"
  1. Change project in the first line to pods_project
  2. Change target name to Tweaks. No "Pods-ProjectName" prefix.

After the change:

installer_representation.pods_project.targets.each do |target|
    if target.name == "Tweaks"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment