figure out ld command arguments from xcode run script build phase - xcode

How can I determine what will be the arguments of the ld command in the build process from inside a script running as "run script build phase"?
I was looking at xcodebuild -dry-run as an option, but then I need to understand what should be the arguments I supply it.
Any idea for a robust solution?
EDIT:
It seems that xcodebuild doesn't support LD and LDPLUSPLUS when the project includes swift source code. So the solution that #fpotter offered doesn't work on project with swift.
Any thoughts?

Xcode doesn't expose a nice way to do this. In a Run Script build phase, all you have to work with are the Xcode build settings provided to you in the environment.
If you really need the entire argument list to ld, there's a hack you can use. With the LD and LDPLUSPLUS build settings, you can make Xcode call a script of your own instead of the real ld. From that script, you could capture the args, call through to the real linker, and then do whatever post processing you like there rather than in a Run Script build phase.
Here's how you could do that:
Create an .xcconfig for your target.
It should look like this:
LD = $(SRCROOT)/ld-wrapper/clang
LDPLUSPLUS = $(SRCROOT)/ld-wrapper/clang++
SRCROOT points to your project's directory. The LDPLUSPLUS line is only required if your app has C++ or ObjC++ code. If you don't want to create an xcconfig, you can also add these as User-Defined build settings via the Xcode UI.
Create wrapper scripts for Xcode to call.
Install a script like this at <your project root>/ld-wrapper/wrapper.sh:
#!/bin/bash
set -o errexit
# Choose which clang to run (clang or clang++) depending on how we're invoked.
# If we're invoked via the 'clang' link, we'll run 'clang'. If we're invoked
# via the 'clang++' link, we'll run 'clang++'.
CLANG_PATH="$DEVELOPER_DIR"/Toolchains/XcodeDefault.xctoolchain/usr/bin/$(basename "$0")
"$CLANG_PATH" "$#"
echo "clang args: $#"
echo "do any post processing here."
Create symlinks for the wrapper script for clang and clang++:
cd <project root>/ld-wrapper
ln -s wrapper.sh clang
ln -s wrapper.sh clang++
That's it. It's ugly, but it works.

Related

Linker fails in sandbox when running through Bazel but works when sandboxed command is executed from the command line

I'm trying to get our cross-toolchain (standard Yocto toolchain) working with Bazel. I followed the instructions on https://github.com/bazelbuild/bazel/wiki/Building-with-a-custom-toolchain but the linker fails every time I try to build a simple test program. It says
external/toolchain_e6500/sysroots/x86_64-fslsdk-linux/usr/bin/powerpc64-fsl-linux/../../libexec/powerpc64-fsl-linux/gcc/powerpc64-fsl-linux/4.9.2/real-ld: cannot find /lib64/libc.so.6
external/toolchain_e6500/sysroots/x86_64-fslsdk-linux/usr/bin/powerpc64-fsl-linux/../../libexec/powerpc64-fsl-linux/gcc/powerpc64-fsl-linux/4.9.2/real-ld: cannot find /usr/lib64/libc_nonshared.a
external/toolchain_e6500/sysroots/x86_64-fslsdk-linux/usr/bin/powerpc64-fsl-linux/../../libexec/powerpc64-fsl-linux/gcc/powerpc64-fsl-linux/4.9.2/real-ld: cannot find /lib64/ld64.so.1
The sysroot is set properly. When running the linker command outside of the sandbox (e.g. with --spawn_strategy=standalone or just manually) it works always. The strange thing is that when I use --debug_sandbox and run the emitted command from the command line, it works, too.
I've been debugging this issue for two days now, including straceing the Bazel daemon and comparing the real-ld input but I didn't find anything suspicious.
There must be a difference between the execution environments but I'm out of ideas now. Here is the failing command as printed by --debug_sandbox:
(cd /home/sick/.cache/bazel/_bazel_sick/2a7ae5e27644389520091aa03d045c73/execroot/__main__ && \
exec env - \
PATH=/home/sick/bin:/home/sick/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/sick/bin \
PWD=/proc/self/cwd \
TMPDIR=/tmp \
/home/sick/.cache/bazel/_bazel_sick/2a7ae5e27644389520091aa03d045c73/execroot/__main__/_bin/linux-sandbox -t 15 -w /home/sick/.cache/bazel/_bazel_sick/2a7ae5e27644389520091aa03d045c73/sandbox/linux-sandbox/2/execroot/__main__ -w /tmp -w /dev/shm -D -- tools/compiler_e6500/e6500_gcc/powerpc64-fsl-linux-gcc -o bazel-out/e6500-fastbuild/bin/test '--sysroot=external/toolchain_e6500/sysroots/ppc64e6500-fsl-linux' -no-canonical-prefixes -pie -Wl,-z,relro,-z,now -Wl,-S -Wl,#bazel-out/e6500-fastbuild/bin/test-2.params)
You can take a look at the workspace here https://github.com/jasal82/bazel-cross-eval
I can provide the toolchain if needed.
UPDATE 2018-09-25
I did some more investigation after reading the answer regarding the linker script below. Section 4.4.2 in this manual says that
In case a sysroot prefix is configured, and the filename starts with
the / character, and the script being processed was located inside the
sysroot prefix, the filename will be looked for in the sysroot prefix.
Otherwise, the linker will try to open the file in the current
directory.
Obviously the script is not being considered to be located inside the sysroot prefix by ld and thus used literally (i.e. resolved relative to the current dir, probably the sandbox root). That would explain the observed behavior. However, I still don't understand why it does not happen when I run the command manually, i.e. not through the Bazel daemon.
Could this be related to the relative sysroot path used in the CROSSTOOL file? As far as I understood you cannot specify an absolute path to the sysroot due to the sandboxing. What is the recommended way to handle this in Bazel? I would like to avoid having to patch the toolchain.
Have a look at lib.so in your sysroot. It probably looks something like this:
/* GNU ld script
Use the shared library, but some functions are only in
the static library, so try that secondarily. */
OUTPUT_FORMAT(elf64-x86-64)
GROUP ( /lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 )
Change the paths in it to be relative to the directory that libc.so is in. You'll have to perform a similar operation on libm.so and libpthread.so.
The GNU linkers resolve symlinks before evaluating whether a script is within the sysroot. The current Bazel Linux sandbox implementation makes a symlink farm of all an action's inputs. Thus, the libc.so.6 linker script is detected as being in the sysroot outside the sandbox but not within the sandbox.

Is there any way to get an XCode build setting to vary according to build ACTION (e.g. clean, rebuild)?

I'm trying to figure out exactly how schemes work in xcode and what they're for. I have a cross-platform product that's built on OS X using an external build system ( scons ). I'd like to be able to build/debug it from Xcode, mostly because of the symbol search and the debugger. I've been using eclipse CDT which mostly works well, but has some quirks.
I can mostly get this to work by creating an empty project and adding an 'external build system' target. Then, as part of the 'Info' of the target, I specify the 'Build Tool' as /usr/local/bin/scons, and the 'Arguments' are the build parameters that I send to scons. Basically I have the following build variables called $(TARGET) and $(BUILD_TYPE) that vary according to whether the build is debug or release, so those can be specified as conditional 'Build Settings'.
The problem is I'd like Menu->Project->Clean to work. It looks like Xcode/xcodebuilder use the $(ACTION) variable to pass this on - where $(ACTTION) is either 'build', 'clean', or some other build actions. See xcodebuild ACTION. Scons is a bit different - it has a built-in clean action that's invoked on the command line with scons -c. So my first thought was to use a conditional 'Build Setting' to pass this parameter, but it turns out that conditional 'Build Settings' don't seem to vary based on the build ACTION - just the build architecture and SDK.
Is it possible to add an expression to a 'Build Setting' in Xcode/xcodebuilder? Is there another good way that I could get 'Clean' to work in Xcode with scons?
Write a wrapper script for SCons, and put it in your project. For example:
External Build Tool Configuration
Build Tool: $(PROJECT_DIR)/scons-xcode-wrapper.sh
Arguments: $(ACTION)
Wrapper Script
From an experiment, it looks like $(ACTION) is empty when building, and set to clean when cleaning.
#!/bin/sh
cd "$PROJECT_DIR"
case $1 in
clean)
scons -c
;;
*)
scons
;;
esac
Don't forget to chmod +x your script.
With SCons, you can programatically set the build to perform a clean using the SetOption() function. The clean option and others that can be set are listed here.
The problem is that SCons treats command line options that arent in the form "--option" or "option=value" as targets. So its not possible to cause something like scons clean to perform a clean. I tested with the Alias() function and could not get it to work.
If you have the option to change the string that xcode uses for $(ACTION) to something like clean=1 then you could something like the following to programatically do a clean:
env = Environment()
if ARGUMENTS.get('clean') == '1':
print "Setting clean"
env.SetOption('clean', True)
print env.GetOption('clean')
...
This will cause a clean:
scons clean=1
You could also consider the AddOption(), but this only allows for options with -- prepended
Set your 'Arguments' parameter to end with:
--$(ACTION)
This will translate to:
scons [all your arguments] --clean
If you check the docs for scons here, you can see that --clean works just as well as -c
Clean is the only one that works however, the other build actions always come through blank.
You can use a combination of the answers above, namely:
Set your arguments to something like --xcode-action=$(ACTION).
Put code in your SConstruct like this:
env.AddOption('--xcode-action',
dest='xcode_action',
type='string',
action='store',
default='',
help='The action Xcode wishes to perform')
if GetOption('xcode_action') == 'clean':
SetOption('clean', True)
This is basically the solution I'm using in scons-xcode.

Is there an easy way to COLOR-CODE the compiler outputs?

gcc (or other compilers) often generate huge text output and it's very difficult to see where the error is or miss warnings. I've done some search but havn't found a clean simple solution to color code the compiler output (so for instance warnings are yellow, errors are red, etc...)
Gcc 4.9 seems to have added this feature via the -fdiagnostics-color flag:
here's an alternative if you are looking for something very simple:
#!/bin/bash -e
make ${#} 2>&1 | perl -wln -M'Term::ANSIColor' -e '
m/Building|gcc|g++|\bCC\b|\bcc\b/ and print "\e[1;32m", "$_", "\e[0m"
or
m/Error/i and print "\e[1;91m", "$_", "\e[0m"
or
m/Warning/i and print "\e[1;93m", "$_", "\e[0m"
or
m/Linking|\.a\b/ and print "\e[1;36m", "$_", "\e[0m"
or
print; '
Just alias your make to this script and make sure it's executable...
Debian and Ubuntu gives the colorgcc package for that purpose.
And I usually run gcc (and make) thru emacs with M-x compile then the messages are colorized.
addenda
GCC 4.9 has a native colorization facility and GCC 6 - released end of April 2016 - (and probably GCC 5 too) is enabling it by default (when stdout is a terminal).
Ok, I'll just leave a notice about my own (python based) tool also :)
It is called Pluggable Output Processor and designed not only to colorize output of one particular program. Here is sample GCC output before:
After:
See colorgcc, a perl script that coulours the gcc output.
How to install and use colorgcc to colorize your gcc compiler output:
At least 3 answers here so far mention colorgcc, but NONE OF THEM EXPLAIN HOW TO INSTALL IT! (And it's not obvious). So, here's how to install the latest version in Ubuntu!
Go here and click "Clone or download" --> "Download Zip". I saved it into "~/Downloads/Install_Files"
Navigate to it in your file browser and right click it and go to "Extract Here." I now have a directory called "~/Downloads/Install_Files/colorgcc-master".
Copy the "colorgcc.pl" script to "/usr/bin/colorgcc" to "install" it (be sure to use the correct directory according to where you extracted it above): sudo cp ~/Downloads/Install_Files/colorgcc-master/colorgcc.pl /usr/bin/colorgcc
Make it executable: sudo chmod +x /usr/bin/colorgcc
Make the "~/bin" directory if it does not yet exist: mkdir ~/bin
*Make symbolic links that point to "/usr/bin/colorgcc" so that whenever you call gcc or g++ it automatically calls colorgcc instead:
ln -s /usr/bin/colorgcc ~/bin/g++
ln -s /usr/bin/colorgcc ~/bin/gcc
(if you ever want to uninstall colorgcc for some reason just delete these symbolic links "~/bin/g++" and "~/bin/gcc", and the Perl script: "/usr/bin/colorgcc" and you're done)
Done!
Here is a sample g++ output now when I call g++ -Wall -std=c++11 time_until_overflow_2.cpp -o time_until_overflow_2:
*Note: making these symbolic links in "~/bin" only works if "~/bin" is in your PATH variable in a location before the folder where the actual gcc and g++ executables are located. To ensure you have "~/bin" in your path you can view the PATH variable contents with: echo $PATH. If you don't see "/home/YOUR_USERNAME/bin" at the beginning of your path, add it with: export PATH=~/bin:$PATH.
References:
See here for more info. and for where I originally learned most of these steps: https://imranfanaswala.wordpress.com/2009/02/02/setting-up-colorgcc/. Thanks Imran Fanaswala!
~GS
you can use GilCC which is a Ruby tool that will convert GCC output to color in real-time. Right now you have two choices: Perl script (colorGCC) or GilCC and if you already work with Ruby you will like GilCC.
Unique to GilCC; GilCC has warning and errors counters and also shows compile time, very handy when you are trying to improve things. Because it is in Ruby it is cross platform. It is flexible and you can add more gems to customize it anyway you want.
The link to the download page is here.
https://github.com/gilmotta/GilCC
Although GCC 4.9 has -fdiagnostics-color option to enable colored outputs to terminals, I have created a tiny tool called 'crror' to get colorized compiler output.
It supports outputs from make as well. I can add colorize patterns for other tools if anyone requires.

PATH variable when calling make from XCode

For an iPad application I need to transform some CoffeeScript files into JavaScript files before bundling them with the application.
I tried to add a Makefile to my XCode project with the following code:
MANUAL_ROOT=IOS12BSH/manual
SCRIPTS_ROOT=$(MANUAL_ROOT)/scripts
COFFEE_SOURCES=$(SCRIPTS_ROOT)/*.coffee $(SCRIPTS_ROOT)/guides/*.coffee
JAVASCRIPT_TARGETS=$(COFFEE_SOURCES:.coffee=.js)
all: build
build: coffeescript
clean: clean_coffeescript
coffeescript: $(JAVASCRIPT_TARGETS)
clean_coffeescript:
rm -f $(JAVASCRIPT_TARGETS)
$(JAVASCRIPT_TARGETS): $(COFFEE_SOURCES)
coffee -c $(COFFEE_SOURCES)
Running this Makefile from the shell works without problems. However, after I added the Makefile as a target in XCode, I ran into problems.
The following error was produced by the Makefile:
coffee -c IOS12BSH/manual/scripts/*.coffee IOS12BSH/manual/scripts/guides/*.coffee
/bin/sh: coffee: command not found
make: *** [IOS12BSH/manual/scripts/*.js] Error 127
Command /Applications/Xcode.app/Contents/Developer/usr/bin/make failed with exit code 2
That is strange, as the coffee command is installed on my machine (it is installed under /opt/local/bin/coffee and /opt/local/bin is added to my $PATH in ~/.profile).
So I added an echo $(PATH) to my Makefile and it seems that the $PATH is different, when the Makefile is executed by XCode. XCode does not seem to read the settings from ~/.profile and therefore /opt/local/bin is not in $PATH.
What is the reason for this and how can I fix this, so that the coffee command is found?
Well, it seems that programs started via the Dock or Spotlight do not execute .profile and therefore $PATH is not set correctly.
So one way would be to set the $PATH in ~/.MacOSX/environments.plist. That works then apparently, but you will need a restart before it works.
Another way is to start XCode always from the command line with open projectfile.
This answer explains the problem in detail:
https://stackoverflow.com/a/14285335/751061
What ended up working best for me is just to launch Xcode from the command line.
I wrote a simple bash script that looks like:
source ~/.bash_profile # This is the trick that gets us our environment variables.
open -a "Xcode"
And then I call it from an Applescript Application just to give it a bundle I could put on the dock:
do shell script "~/xcode_launcher"
Sourcing your profile is necessary in the bash script, because running a script from Applescript doesn't ever source from a profile, so you still wouldn't have your default environment variables.
SAme thing with ant command. It works on terminal, not if Xcode have to do it. Only way to got it work: sudo open project.xcodeproj in terminal.

Eclipse CDT: pkg-config indexing

I`m developing a application using gtkmm with eclipse. While I could have setup gtkmm include paths and linking options manually, i decided to let pkg-config do the work because of the huge number of referenced projects. This was quite easy as adding the appropriate pkg-config command to the compiler invocation worked just fine because one can simply use the
`...`
shell substitution since eclipse will generate a makefile which is then executed.
Setting up the indexer right isnt that easy though. Instead of executing shell script in a interpreter, eclipse executes the compiler directly and pass command line arguments directly without substituting them before.
How can one execute shell script when executing the indexer?
The solution is to execute the bash interpreter with the -c flag directly instead of executing g++.
For the scenario described in the question the configurations are as followed:
Compiler invocation command
bash
Compiler invocation arguements
-c "g++ `pkg-config gtkmm-2.4 --cflags` -E -P -v -dD ${plugin_state_location}/specs.cpp"

Resources