Xcode -> Makefile
Jim Schimpf
(vze35xda@verizon.net)(7-Mar-2004)
When I develop on OS X I usually use X Code. Others obviously
differ about use of IDE's but I prefer it. I do have a problem
when I wish to move an app from OS X to other flavors of UNIX or
Linux. In most cases I am not attempting anything exotic, these
are usually CLI applications (e.g. a very specialized Lua interpreter
with extensions for my company's products). So moving to Linux or
UNIX should be pretty much a slam-dunk, just recompiling the
code. Easy in theory but the app in question has over 100 files,
files in many different directories and it's an X Code project so no
makefile.
I had hoped that X Code would have this export function but it didn't,
so I created a free open source version is available to you.
PBTOMAKE Version 3.0 Program
This program is freeware with source and can be
downloaded from (PBTOMAKE
Dowload page). It is run from the the terminal command line
and as an example run on its own project you would get:
[Pandoras-Computer:TOMAKE10.3/Source/PBTOMAKE] jim% pbtomake -i PBTOMAKE.pbproj
PRODUCT IS [pbtomake]
FILE [main.cp]
FILE [CPBPROJ.cp]
FILE [CBufFile.cp]
FILE [CSymbol.cp]
FILE [CLexFile.cp]
FILE [CpbxLexFile.cp]
FILE [CTokFile.cp]
FILE [CMaker.cp]
FILE [cutil.cp]
FILE [CBufFile.h]
FILE [tgtypes.h]
FILE [CSymbol.h]
FILE [CLexFile.h]
FILE [CpbxLexFile.h]
FILE [CTokFile.h]
FILE [CMaker.h]
FILE [base.h]
FILE [cutil.h]
FILE [CPBPROJ.h]
[Pandoras-Computer:TOMAKE10.3/Source/PBTOMAKE] jim%
This would generate the make file in a file called makefile. The
makefile could be used by typing make in this directory. This would
build a new copy of pbtomake using make
and the makefile.
[Pandoras-Computer:TOMAKE10.3/Source/PBTOMAKE] jim% make
/usr/bin/g++3 ../main.cp -c -I../BASE_PC -I../BASE_PC/Dohickies -I.. -o main.o
/usr/bin/g++3 ../CPBPROJ.cp -c -I../BASE_PC -I../BASE_PC/Dohickies -I.. -o CPBPROJ.o
/usr/bin/g++3 ../BASE_PC/CBufFile.cp -c -I../BASE_PC -I../BASE_PC/Dohickies -I.. -o CBufFile.o
:
:
/usr/bin/g++3 ../CMaker.cp -c -I../BASE_PC -I../BASE_PC/Dohickies -I.. -o CMaker.o
/usr/bin/g++3 ../BASE_PC/cutil.cp -c -I../BASE_PC -I../BASE_PC/Dohickies -I.. -o cutil.o
/usr/bin/g++3 \
main.o\
CPBPROJ.o\
CBufFile.o\
CSymbol.o\
CLexFile.o\
CpbxLexFile.o\
CTokFile.o\
CMaker.o\
cutil.o\
-o pbtomake
[Pandoras-Computer:TOMAKE10.3/Source/PBTOMAKE] jim%
Pretty simple really, and the idea is you run pbtomake on your project, copy the
whole development tree to the Linux/UNIX box and run make there. You will then get
the application built on the other system without having to manually
build a make file with all the dependencies. Obviously you still
have code porting to do since for example under OS X you link to
frameworks while under other systems you have other ways. There's
lots of other details involved in porting the code but pbtomake saves
you initial messy task of listing all the files and their paths to
manually build a makefile.
makefile Details
The makefile created by the program is very simple and has everything
specified, It doesn't use rules or any other shortcuts. Each and
every file is separately specified along with all the include
files. So the file created for this run is:
###################################################
#
# Makefile for pbtomake
# Creator [Xcode -> Makefile Ver: 3.00]
# Created: [Sat Mar 6 07:48:15 2004]
#
###################################################
#
# Macros
#
CC = /usr/bin/g++3
CC_OPTIONS =
LNK_OPTIONS =
#
# INCLUDE directories for pbtomake
#
INCLUDE = \
-I../BASE_PC\
-I../BASE_PC/Dohickies\
-I..
#
# Build pbtomake
#
pbtomake : \
main.o\
:
:
cutil.o
$(CC) $(LNK_OPTIONS) \
main.o\
:
:
cutil.o\
-o pbtomake
#
# Build the parts of pbtomake
#
# Item # 1 -- main --
main.o : ../main.cp
$(CC) $(CC_OPTIONS) ../main.cp -c $(INCLUDE) -o main.o
:
:
# Item # 9 -- cutil --
cutil.o : ../BASE_PC/cutil.cp
$(CC) $(CC_OPTIONS) ../BASE_PC/cutil.cp -c $(INCLUDE) -o cutil.o
##### END RUN ####
Program Internals
Project Files
The Xcode project file PBTOMAKE.pbproj is really a
directory containing two files (do
ls -als PBTOMAKE.pbproj at the
command line):
0 drwxrwxrwx 4 jim staff 136 7 Mar 07:03 .
0 drwxrwxrwx 18 jim staff 612 6 Mar 07:51 ..
40 -rwxrwxrwx 1 jim staff 17747 7 Mar 07:03 jim.pbxuser
32 -rwxrwxrwx 1 jim staff 12708 7 Mar 07:03 project.pbxproj
The jim.pbxuser
is the per user settings for the project and things like window
positions. The project.pbxproj is the description of the files
and how to build the application. This is the file that pbtomake works on.
Knowledge of project.pbxproj
is just by observation and trying to extract the information to build
the makefile. If someone reading this has deeper knowledge I
would appreciate hearing from you. The general form of both of
these files is a hierarchal structure based on keys/values and
lists. The usual form of things are:
key = value;
Now value can either be a single string/name or a
list and lists are written as:
{
thing,
thing1,
:
};
Where thing and thing1 can be either simple values
or embedded lists.
Project File Analysis
The code modules CBPROJ,
CpbxLexFile, CTokFile, CLexFile and CBufFile are used to convert the
file structure into an in memory tree representation of the text
structure in the file. The form here is the conventional breaking
of the input file into tokens; then CBPROJ.cp
builds the tree.
For example reading key =
value; would turn into the token list
key = value ;
<Token> <LINK> <Token> <END>
CBPROJ reads each of these
tokens, stacking them up until a trigger is found, In this case an
<END> token meaning we have come to the end of an element in the
file. It then can package up the key and value as an element and
attach it to the tree structure of the file it's building. If you
want to see this tree use the -debug option of pbtomake and it will be dumped out.
Besides the tree structure being built, the program also needs a
reference table so it can search this tree for particular keys. A
direct search of the tree might work but, from experience this is very
slow. The module CSymbol
is used to create a hashed symbol table of each of the keys found in
the file. In this way, a very quick search can find nth
appearance of any key in the file.
Makefile Construction
The module CMaker is where the
rubber meets the road and the actual makefile is constructed.
This module is run after the symbol table and tree representation of
the project file are completed. To build the makefile two things
have to be found: a list of the .H files and a list of the .C(or CP)
files. You can see these in the product makefile. Once you
know these then:
The INCLUDE list comes from the path lists to the .h
files
The main dependency <name> :
<Filename>.o comes from the list of C/CP files
The main link list and each of the individual file
builds comes from the .c list.
I've found in the project file the VALUE of an isa key of PBXSourcesBuildPhase denotes the
list of C files and PBXHeadersBuildPhase
denotes the list of H files. It is then a matter of finding these lists
in the in memory tree representation of the project file and building
file lists.
The rest of building the makefile is largely a matter of fprintf()'s of
the lists of data in various forms to the output makefile. This
is also where if you want another type of makefile it's quite easy to
change the output.