Distributing .app file after command line xcodebuild call - macos

I'm building/archiving my Mac app for distribution from a command line call (below), with Xcode 4.3 installed. To be clear, I didn't have a working solution for this problem earlier to Xcode 4.3, so advice for earlier Xcode releases could easily still be valid. Here's the call:
/usr/bin/xcodebuild -project "ProjectPath/Project.pbxproj" -scheme "Project" -sdk macosx10.7 archive
This runs successfully, and it generates an .xcarchive file, located in my ~/Library/Developer/Xcode/Archives/<date> folder. What's the proper way to get the path the the archive file generated? I'm looking for a way to get a path to the .app file contained therein, so I can distribute it.
I've looked at the MAN page for xcodebuild (and done copious searching online) and didn't find any clues there.

There is an easier way, simply specify the archivePath you want to archive:
xcodebuild -archivePath GoTray -scheme GoTray archive
Then you will get the xcarchive file at GoTray.xcarchive in current directory.
Next, run xcodebuild again to export app from the xcarchive file:
xcodebuild -exportArchive -exportFormat APP -archivePath GoTray.xcarchive -exportPath GoTray

Building on the answer provided here, I came up with a satisfactory multi-part solution. The key to it all, was to use the environment variables Xcode creates during the build.
First, I have a post-action on the Archive phase of my build scheme (pasted into the Xcode project's UI). It calls a Python script I wrote (provided in the next section), passing it the names of the environment variables I want to pull out, and a path to a text file:
# Export the archive paths to be used after Archive finishes
"${PROJECT_DIR}/Script/grab_env_vars.py" "${PROJECT_DIR}/build/archive-env.txt"
"ARCHIVE_PATH" "ARCHIVE_PRODUCTS_PATH" "ARCHIVE_DSYMS_PATH"
"INSTALL_PATH" "WRAPPER_NAME"
That script then writes them to a text file in key = value pairs:
import sys
import os
def main(args):
if len(args) < 2:
print('No file path passed in to grab_env_vars')
return
if len(args) < 3:
print('No environment variable names passed in to grab_env_vars')
output_file = args[1]
output_path = os.path.dirname(output_file)
if not os.path.exists(output_path):
os.makedirs(output_path)
with open(output_file, 'w') as f:
for i in range(2, len(args)):
arg_name = args[i]
arg_value = os.environ[arg_name]
#print('env {}: {}'.format(arg_name, arg_value))
f.write('{} = {}\n'.format(arg_name, arg_value))
def get_archive_vars(path):
return dict((line.strip().split(' = ') for line in file(path)))
if __name__ == '__main__':
main(sys.argv)
Then, finally, in my build script (also Python), I parse out those values and can get to the path of the archive, and the app bundle therein:
env_vars = grab_env_vars.get_archive_vars(ENV_FILE)
archive_path = env_vars['ARCHIVE_PRODUCTS_PATH']
install_path = env_vars['INSTALL_PATH'][1:] #Chop off the leading '/' for the join below
wrapper_name = env_vars['WRAPPER_NAME']
archived_app = os.path.join(archive_path, install_path, wrapper_name)
This was the way I solved it, and it should be pretty easily adaptable to other scripting environments. It makes sense with my constraints: I wanted to have as little code as possible in the project, I prefer Python scripting to Bash, and this script is easily reusable in other projects and for other purposes.

You could just use a bit of shell, get the most recent folder in the Archives dir (or use the current date), and then get the most recent archive in that directory.

Related

How to ship or export an sdl project made in codeblocks

The project uses external image files and codeblocks is configured to find the library and header files for SDL. How can I turn the project into a format so that it can be easily shipped ? I intend to create a single executable file or an installer which is portable .
How can I do this ?
I've done that many times.
You just have to put the necessary DLLs (I use SDL 1.2, so SDL.dll, SDL_image.dll,zlib.dll, ...) where the executable is, which may be not so good with codeblocks/VC++ tree and the Debug/bin & Release/bin directories but would work with a .bat file which could run the executable.
OR
Consider that your development project is NOT the one the users will see.
Do you think that Microsoft guys code all system32 commands in one single source directory?
So develop however you like and then create a deliver script to put everything in the root directory. Like that you don't have the constraints of delivered product while you are developping and reverse.
I recommend this approach.
Checkout My bagman SDL remake to see what I mean
When I create the archive for distribution, I use a custom script that I include here. This one has all the works, libmpeg for mp3 decompression, and all. If you unzip the Bagman archive, it works right away on a windows PC.
Here's my directory tree
Bagman\bin
Bagman\exploit
Bagman\icons
Bagman\music
Bagman\obj
Bagman\resource
Bagman\sound
Bagman\src
Bagman\work
Bagman\bin\Debug
Bagman\bin\Debug256
Bagman\bin\Release
Bagman\exploit\mkf
Bagman\obj\Amiga
Bagman\obj\Debug
Bagman\obj\Debug256
Bagman\obj\NDS
Bagman\obj\NDSEMU
Bagman\obj\Release
Bagman\obj\Debug\src
Bagman\obj\Debug\src\characters
Bagman\obj\Debug\src\engine
Bagman\obj\Debug\src\gfx
Bagman\obj\Debug\src\screens
Bagman\obj\Debug\src\sys
Bagman\obj\Debug256\src
Bagman\obj\Debug256\src\characters
Bagman\obj\Debug256\src\engine
Bagman\obj\Debug256\src\gfx
Bagman\obj\Debug256\src\sys
Bagman\obj\NDS\src
Bagman\obj\NDS\src\characters
Bagman\obj\NDS\src\engine
Bagman\obj\NDS\src\gfx
Bagman\obj\NDS\src\sys
Bagman\obj\NDSEMU\src
Bagman\obj\NDSEMU\src\characters
Bagman\obj\NDSEMU\src\engine
Bagman\obj\NDSEMU\src\gfx
Bagman\obj\NDSEMU\src\sys
Bagman\obj\Release\src
Bagman\obj\Release\src\characters
Bagman\obj\Release\src\engine
Bagman\obj\Release\src\gfx
Bagman\obj\Release\src\sys
Bagman\resource\1x
Bagman\resource\maps
Bagman\resource\1x\images
Bagman\resource\1x\sprites
Bagman\src\characters
Bagman\src\engine
Bagman\src\gfx
Bagman\src\screens
Bagman\src\sys
Here's the python script that copies all necessary files in the user archive:
import sys,os,zipfile,re,shutil,glob
import find,which # custom modules I created myself
MODULE_FILE = sys.modules[__name__].__file__
PROGRAM_DIR = os.path.abspath(os.path.dirname(MODULE_FILE))
ROOTDIR=os.path.realpath(os.path.join(PROGRAM_DIR,os.pardir))
PRODUCT_NAME="Bagman"
music=False
dev=False
version = "1.2"
print("making archive for version "+version)
os.chdir(ROOTDIR)
archname=PRODUCT_NAME+"-win32-"+version+".zip"
zfn = os.path.join(ROOTDIR,os.pardir,archname)
if os.path.exists(zfn):
os.remove(zfn)
zf = zipfile.ZipFile(zfn,mode="w",compression=zipfile.ZIP_DEFLATED)
dll_list = ["SDL.dll","SDL_Mixer.dll"]+["smpeg.dll","libgcc_s_sjlj-1.dll","libstdc++-6.dll","libgcc_s_dw2-1.dll"] # MP3 playing needs the second part
items_list = ["bagman*.bat","*.cbp","*.dev","*.txt","COPYING","resource","exploit","sound","src"]
real_items_list = []
for i in items_list:
real_items_list.extend(glob.glob(i))
for d in dll_list:
print("processing '"+d+"'")
dp = which.which(d)
if len(dp)==0:
raise Exception("%s not found in PATH" % d)
zf.write(dp[0],arcname=PRODUCT_NAME+"/"+d)
for i in real_items_list:
print("processing '"+i+"'")
if os.path.isfile(i):
zf.write(i,arcname=PRODUCT_NAME+"/"+i)
else:
fnd = find.Find()
items = fnd.init(i)
for f in items:
if i == "resource" and os.path.basename(f) in ["settings","soundbank.bin"]:
pass
else:
fi = f.replace(os.sep,"/")[2:]
if os.path.isfile(fi):
if fi.endswith("~"):
pass
else:
zf.write(fi,arcname=PRODUCT_NAME+"/"+fi)
else:
zfi = zipfile.ZipInfo(PRODUCT_NAME+"/"+fi+"/")
zf.writestr(zfi, '')
zfi = zipfile.ZipInfo(PRODUCT_NAME+"/bin/")
zf.writestr(zfi, '')
zfi = zipfile.ZipInfo(PRODUCT_NAME+"/bin/Release/")
zf.writestr(zfi, '')
zfi = zipfile.ZipInfo(PRODUCT_NAME+"/bin/Debug/")
zf.writestr(zfi, '')
zf.write("bin/Release/bagman.exe",arcname=PRODUCT_NAME+"/bin/Release/bagman.exe")
zf.close()
I know that there are existing python modules which will help you achieve such things (distutils). I did not use them because I wasn't aware of them.

Is it possible to sort the Compile Sources list in the Build Phases section of an Xcode project?

I want to sort the files in the 'Compile Sources' section of my Xcode project according to their names. Is it possible?
Yes, you can reorder the Compile Sources section in Xcode, but not from the GUI - which is a shame considering that this is already version 6 of the IDE and they still haven't gotten around to this basic feature.
As A-Live said, you need to edit the project.pbxproj file within the yourproject.xcodeproj file. Use Finder to select the yourproject.xcodeproj file and then use the context menu to Show Package Contents. After that open the project.pbxproj file with a text editor.
Find the PBXSourcesBuildPhase section and copy everything between files = ( and ); into a new text file. Remove the leading tabs/spaces. Save that file somewhere on your disk. Open up a terminal and do this:
sort -bf -t " " -k 3 PBXSourcesBuildPhase.txt > PBXSourcesBuildPhase.sorted.txt
Open up the new PBXSourcesBuildPhase.sorted.txt file in your text editor, copy the sorted lines into the PBXSourcesBuildPhase section of your project.pbxproj (overwrite the lines that you previously copied) and save.
Now you should be able to see all the files sorted in the Compile Sources section in Xcode.
I've tested this in Xcode 6.0.1 with a small project (~150 source files) and had no problems.
Careful: you should make a backup of your project file (or better: use version control) before you try this. Just in case.
I reckon it is a shame that this is not possible.
as a workaround in most of situations, you can use the search filter on the right upper corner of the file list.
for example, I needed to add a compiler flag in many files which (fortunately) all started with the same prefix. to do so, as stated here, you have to double click on a file.
then, I filtered the files for the prefix, shift-clicked them in order to select them all, then released shift and double-clicked one of them. this way I was able to add the flag to all of the files at once
The accepted solution works fine, but it requires manual steps(open the project file, find the section for the target that you want etc.) so it is a little cumbersome and it can not be automated if you need to keep the section sorted each time you perform a build or commit.
I faced with the same problem and I created a ruby script to sort these sections. The script sorts the 'Compile Sources', 'Copy Bundle Resources’ and all the 'Copy files' sections under Build Phase for a specified or all the targets.
#!/usr/bin/env ruby
require 'xcodeproj'
require 'set'
project_file, target_name = ARGV
# open the project
project = Xcodeproj::Project.open(project_file)
# find the target
targets_to_sort = project.native_targets.select { |x| x.name == target_name || target_name.nil? }
phases_to_sort = [Xcodeproj::Project::Object::PBXSourcesBuildPhase, Xcodeproj::Project::Object::PBXCopyFilesBuildPhase, Xcodeproj::Project::Object::PBXResourcesBuildPhase]
targets_to_sort.each do |target|
puts "sorting files for target #{target.name}"
phases_to_sort.each do |phase_to_sort|
target.build_phases.select { |x| x.class == phase_to_sort }.each do |phase|
phase.files.sort! { |l, r| l.display_name <=> r.display_name }
end
end
end
puts 'saving project'
project.save
To sort all targets:
./sort_sources.rb MyProject.xcodeproj
Or to sort only one target:
./sort_sources.rb MyProject.xcodeproj My_Target
It requires the gem xcodeproj:
gem install xcodeproj
This is thoroughly answered, but I thought I'd share the Emacs command that sorted these in place for me. Navigate to project.pbxproj, mark all files under PBXSourcesBuildPhase, and use the command:
M-3 M-x sort-fields
...aka sorting the marked area by the 3rd column, which happens to be the filenames. C-x C-s and you're on your way.
You can reorder the entries of PBXSourcesBuildPhase section at the project.pbxproj, it worked for me but of course there's no guarantee in general for it to work. Don't forget to backup your backups first.

Compiling Z3 on a OSX

I am trying to compile Z3 version 4.1.2. AFter a successful configuration, when you do "make", I get the following error:
Makefile:151: lib.srcs: No such file or directory
Makefile:152: shell.srcs: No such file or directory
Makefile:153: test.srcs: No such file or directory
Making test.srcs...
/usr/local/bin/dos2unix takes only stdin and stdout
make: *** [test.srcs] Error 1
I think the problem is that all textual files in z3-src-4.1.2.zip use "carriage return" (cr) and "line feed" (lf) for encoding line termination. The zip was created on a Windows machine. Another problem is the "dos2unix" application. It is an application that converts windows/dos textual files into unix/linux/osx textual files. It is a very simple application. It just replaces cr/lf with a lf.
On Linux, this application takes a single argument: the file name to be modified.
I'm currently working on a new build system that avoids this issues. In the meantime, here a some workarounds.
1) Use git to retrieve the source. git will take care of the cr/lf vs lf issue.
Here is the command for retrieving Z3:
git clone https://git01.codeplex.com/z3
If you do that, you don't need to use dos2unix.
So, you can remove the lines #$(DOS2UNIX) in Makefile.in. Another option is to replace
DOS2UNIX=#D2U#
with
DOS2UNIX=touch
in the beginning of Makefile.in
After these changes, you should be able to compile it on OSX. I successfully compiled it on OSX 10.7.
2) Get the "unstable" branch.
http://z3.codeplex.com/SourceControl/changeset/view/946a06cddbe4
This is the current "working branch". It contains the new build system. It is not ready, but it is good enough to generate the Z3 executable. Here are the instructions to build Z3 using this branch
Download the code from the page above. Or use git to retrieve the "unstable" branch. Then, execute
autoconf
./configure
python scripts/mk_make.py
cd build
make
I managed to compile it on OSX 10.7 last Friday.
3) Keep the .zip, but convert all textual files. I'm using the following python script to convert all files in the new build system. If you execute this python script in the Z3 root directory, it will convert all files.
import os
import glob
import re
import getopt
import sys
import shutil
def is_cr_lf(fname):
# Check whether text files use cr/lf
f = open(fname, 'r')
line = f.readline()
sz = len(line)
return sz >= 2 and line[sz-2] == '\r' and line[sz-1] == '\n'
# dos2unix in python
# cr/lf --> lf
def dos2unix(fname):
if is_cr_lf(fname):
fin = open(fname, 'r')
fname_new = '%s.new' % fname
fout = open(fname_new, 'w')
for line in fin:
line = line.rstrip('\r\n')
fout.write(line)
fout.write('\n')
fin.close()
fout.close()
shutil.move(fname_new, fname)
if is_verbose():
print "dos2unix '%s'" % fname
def dos2unix_tree_core(pattern, dir, files):
for filename in files:
if fnmatch(filename, pattern):
fname = os.path.join(dir, filename)
if not os.path.isdir(fname):
dos2unix(fname)
def dos2unix_tree():
os.path.walk('.', dos2unix_tree_core, '*')
dos2unix_tree()

Intltool with an autoconf-generated .desktop file

In the Emperor project, I'm having some issues getting intltool to work when doing an out-of-tree build. When running make check out-of-tree, which is one of the things make distcheck does, intltool fails thus:
INTLTOOL_EXTRACT="/usr/bin/intltool-extract" XGETTEXT="/usr/bin/xgettext" srcdir=../../po /usr/bin/intltool-update --gettext-package emperor --pot
can't open ../../po/../data/emperor.desktop.in: No such file or directory at /usr/bin/intltool-extract line 212.
intltool is looking for emperor.desktop.in, which is listed in po/POTFILES.in, in the source tree. However, emperor.desktop.in is generated by the configure script from a file called emperor.desktop.in.in, in order to insert the installed executable path as configured by the user, and lands in the build tree.
These are the relevant bootstrap.sh lines:
echo +++ Running intltoolize ... &&
intltoolize --force --copy &&
cat >>po/Makefile.in.in <<EOF
../data/_column_names.h:
cd ../data && \$(MAKE) _column_names.h
EOF
The setup code in configure.ac:
IT_PROG_INTLTOOL([0.35.0])
GETTEXT_PACKAGE=emperor
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"],
[The domain to use with gettext])
AM_GLIB_GNU_GETTEXT
data/emperor.desktop.in is listed in AC_CONFIG_FILES.
data/Makefile.am contains these lines:
desktopdir = $(datadir)/applications
desktop_in_files = emperor.desktop.in
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
#INTLTOOL_DESKTOP_RULE#
and po/POTFILES.in contains the line
data/emperor.desktop.in
You can review all the details in the public git repository if you wish.
Can I somehow tell intltool that this file will be located in the build tree, not in the source tree? Otherwise, my options appear to be to break make distcheck (not a great option), or to ship a desktop file that doesn't include the full path and assumes that the executable is installed in the PATH. (just as messy, IMHO) - Any other options?
In your source code you have emperor.desktop.in.in, which does not seem to be in any rule as a dependency. That file has to be converted first to emperor.desktop.in and later to emperor.desktop, which does not seem to be the case in your data/Makefile.am.
desktopdir = $(datadir)/applications
desktop_in_in_files = emperor.desktop.in.in
desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
#INTLTOOL_DESKTOP_RULE#
[...]
EXTRA_DIST = \
$(desktop_in_in_files) \
[...]
$(desktop_in_in_files) contains $(desktop_in_in_files), and Makefile will know how to deal with that.
Some further digging has brought me believe that the answer is: intltool does not support source files that aren't source files in the project. Ergo, any additional processing must be done after intltool is through
Intltool requires the lines in POTFILES to be relative to the (build-time) working directory. The file POTFILES is generated by the configure script from POTFILES.in with a simple sed script defined in the IT_PO_SUBDIR autoconf macro (called by IT_PROG_INTLTOOL) that simply prepends the relative location of the top-level source directory to the paths. Alas, modifying POTFILES does not help: the intltool-extract script does everything it can to get the source directory right. I don't believe files that are sometimes inside and sometimes outside the source tree can be supported without modifying intltool itself.

llv8call on Mac OS X - 2nd try

I didn't give a lot of info in my last question.
I've built llv8call from http://code.google.com/p/llv8call/, v0.4. I've gotten the known dependencies installed, being libxml-2.0 and libreadline. My dev system is Mac OS X 10.5. llv8call is built with Scons.
When I attempt to run llv8call via ./llv8call, I get this error:
library loading error: org.coderepos.env is not found in : (loadBinary)
I am not sure how to troubleshoot this error. The author hasn't responded to me yet. I need some tips on how to troubleshoot this more than an explicit answer, though if someone has one it's very welcome.
The files are installed to /usr/local/llv8call. There is a directory structure under llv8call/lib/llv8call/org/coderepos but it doesn't contain an "env" directory. My first guess is that whatever library its looking for at org.coderepos.env is supposed to be in this path, but "env" doesn't exist. If this sounds reasonable, it might be a place that I should start looking at but I need confirmation.
Your intuition seems right. Doing a grep:
grep -r "org.coderepos" *|less
I see it checks for many "libraries" under org.coderepos. Furthermore, in src/main.cc in the preload_builtin_classes function, we see:
Handle<Value> args[1];
args[0] = String::New("org.coderepos.fs");
loadBinary->Call(v8ext, 1, args);
args[0] = String::New("org.coderepos.env");
loadBinary->Call(v8ext, 1, args);
if (try_catch.HasCaught()) {
String::Utf8Value error(try_catch.Exception());
fprintf(stderr, "library loading error: %s\n", *error);
exit(2);
}
That, my friend, is your smoking gun.
It is looking for a library called env (i.e., libenv.so) in the directory /org/coderepos.
Either make /org/coderepos and copy the libraries into it, or ask your dynamic linker to look for /org/coderepos content elsewhere.
I fixed this by doing the following from the top-level directory of my llv8call source directory (after running scons to build everything):
mkdir -p out/lib/llv8call/org/coderepos
find ext -name \*.dylib -exec cp {} out/lib/llv8call/org/coderepos \;
"dtruss -f test.sh" was helpful in finding where v8 was looking for the libs.

Resources