1##---------------------------------------------------------------------
2# Makefile for python (supporting multiple versions)
3##---------------------------------------------------------------------
4Project = python
5VERSIONERDIR = /usr/local/versioner
6FIX = $(SRCROOT)/fix
7DEFAULT = 2.7
8VERSIONS = 2.5 2.6 2.7
9ORDEREDVERS := $(DEFAULT) $(filter-out $(DEFAULT),$(VERSIONS))
10REVERSEVERS := $(filter-out $(DEFAULT),$(VERSIONS)) $(DEFAULT)
11
12PYFRAMEWORK = /System/Library/Frameworks/Python.framework
13PYFRAMEWORKVERSIONS = $(PYFRAMEWORK)/Versions
14VERSIONERFLAGS = -std=gnu99 -Wall -mdynamic-no-pic -I$(DSTROOT)$(VERSIONERDIR)/$(Project) -I$(FIX) -framework CoreFoundation
15
16RSYNC = rsync -rlpt
17PWD = $(shell pwd)
18
19ifeq ($(MAKECMDGOALS),)
20MAKECMDGOALS = build
21endif
22ifneq ($(filter build install,$(MAKECMDGOALS)),)
23ifndef DSTROOT
24ifdef DESTDIR
25export DSTROOT = $(shell mkdir -p '$(DESTDIR)' && echo '$(DESTDIR)')
26else
27export DSTROOT = /
28endif
29endif
30ifndef OBJROOT
31export OBJROOT = $(shell mkdir -p '$(PWD)/OBJROOT' && echo '$(PWD)/OBJROOT')
32RSYNC += --exclude=OBJROOT
33endif
34ifndef SYMROOT
35export SYMROOT = $(shell mkdir -p '$(PWD)/SYMROOT' && echo '$(PWD)/SYMROOT')
36RSYNC += --exclude=SYMROOT
37endif
38endif
39
40ifndef SRCROOT
41export SRCROOT = $(PWD)
42endif
43ifndef RC_ARCHS
44export RC_ARCHS = $(shell arch)
45export RC_$(RC_ARCHS) = YES
46endif
47ifndef RC_CFLAGS
48export RC_CFLAGS = $(foreach A,$(RC_ARCHS),-arch $(A)) $(RC_NONARCH_CFLAGS)
49endif
50ifndef RC_NONARCH_CFLAGS
51export RC_NONARCH_CFLAGS = -pipe
52endif
53ifndef RC_ProjectName
54export RC_ProjectName = $(Project)
55endif
56##---------------------------------------------------------------------
57# Before, we used the versioned gcc (e.g., gcc-4.2) because newer compiler
58# would occasionally be incompatible with the compiler flags that python
59# records.  With clang, it doesn't use names with versions, so we just go
60# back to using plain cc and c++.  With 11952207, we will automatically
61# get xcrun support.
62##---------------------------------------------------------------------
63export MY_CC = cc
64export MY_CXX = c++
65
66##---------------------------------------------------------------------
67# The "strip" perl script, works around a verification error caused by a
68# UFS bug (stripping a multi-link file breaks the link, and sometimes causes
69# the wrong file to be stripped/unstripped).  By using the "strip" perl script,
70# it not only causes the correct file to be stripped, but also preserves the
71# link.
72#
73# The cc/c++ scripts take a -no64 argument, which causes 64-bit architectures
74# to be removed, before calling the real compiler.
75##---------------------------------------------------------------------
76export PATH:=$(OBJROOT)/bin:$(PATH)
77
78TESTOK := -f $(shell echo $(foreach vers,$(VERSIONS),$(OBJROOT)/$(vers)/.ok) | sed 's/ / -a -f /g')
79
80include $(MAKEFILEPATH)/CoreOS/ReleaseControl/Common.make
81
82VERSIONVERSIONS = $(VERSIONERDIR)/$(Project)/versions
83VERSIONHEADER = $(VERSIONERDIR)/$(Project)/versions.h
84VERSIONBINLIST = $(VERSIONERDIR)/$(Project)/usr-bin.list
85VERSIONMANLIST = $(VERSIONERDIR)/$(Project)/usr-share-man.list
86VERSIONERFIX = dummy.py scriptvers.ed
87build::
88	$(RSYNC) '$(SRCROOT)/' '$(OBJROOT)'
89	ln -sf _no64 $(OBJROOT)/bin/$(MY_CC)
90	ln -sf _no64 $(OBJROOT)/bin/$(MY_CXX)
91	@set -x && \
92	for vers in $(VERSIONS); do \
93	    mkdir -p "$(SYMROOT)/$$vers" && \
94	    mkdir -p "$(OBJROOT)/$$vers/DSTROOT" && \
95	    (echo "######## Building $$vers:" `date` '########' > "$(SYMROOT)/$$vers/LOG" 2>&1 && \
96		TOPSRCROOT='$(SRCROOT)' \
97		$(MAKE) -C "$(OBJROOT)/$$vers" install \
98		SRCROOT="$(SRCROOT)/$$vers" \
99		OBJROOT="$(OBJROOT)/$$vers" \
100		DSTROOT="$(OBJROOT)/$$vers/DSTROOT" \
101		SYMROOT="$(SYMROOT)/$$vers" \
102		RC_ARCHS='$(RC_ARCHS)' >> "$(SYMROOT)/$$vers/LOG" 2>&1 && \
103		touch "$(OBJROOT)/$$vers/.ok" && \
104		echo "######## Finished $$vers:" `date` '########' >> "$(SYMROOT)/$$vers/LOG" 2>&1 \
105	    ) & \
106	done && \
107	wait && \
108	install -d $(DSTROOT)$(VERSIONERDIR)/$(Project)/fix && \
109	(cd $(FIX) && rsync -pt $(VERSIONERFIX) $(DSTROOT)$(VERSIONERDIR)/$(Project)/fix) && \
110	echo DEFAULT = $(DEFAULT) > $(DSTROOT)$(VERSIONVERSIONS) && \
111	for vers in $(VERSIONS); do \
112	    echo $$vers >> $(DSTROOT)$(VERSIONVERSIONS) && \
113	    cat $(SYMROOT)/$$vers/LOG && \
114	    rm -f $(SYMROOT)/$$vers/LOG || exit 1; \
115	done && \
116	if [ $(TESTOK) ]; then \
117	    $(MAKE) merge; \
118	else \
119	    echo '#### error detected, not merging'; \
120	    exit 1; \
121	fi
122
123##---------------------------------------------------------------------
124# After the rest of the merge, we need to merge /usr/lib manually.  The
125# problem is that now libpythonN.dylib is ambiguous, and probably best
126# to just avoid it, and just keep libpythonN.M.dylib.  libpython.dylib
127# will correspond with the default version.
128##---------------------------------------------------------------------
129MERGELIB = /usr/lib
130merge: mergebegin mergedefault mergeversions mergeplist mergebin mergeman fixsmptd
131	install -d $(DSTROOT)$(MERGELIB)
132	@set -x && \
133	for vers in $(VERSIONS); do \
134	    ln -sf ../..$(PYFRAMEWORKVERSIONS)/$$vers/Python $(DSTROOT)$(MERGELIB)/lib$(Project)$$vers.dylib && \
135	    ln -sf ../..$(PYFRAMEWORKVERSIONS)/$$vers/lib/$(Project)$$vers $(DSTROOT)$(MERGELIB)/$(Project)$$vers || exit 1; \
136	done
137	ln -sf lib$(Project)$(DEFAULT).dylib $(DSTROOT)$(MERGELIB)/lib$(Project).dylib
138
139mergebegin:
140	@echo ####### Merging #######
141
142MERGEBIN = /usr/bin
143TEMPWRAPPER = $(MERGEBIN)/.versioner
144mergebin: $(DSTROOT)$(VERSIONHEADER) $(OBJROOT)/wrappers
145	cc $(RC_CFLAGS) $(VERSIONERFLAGS) $(VERSIONERDIR)/versioner.c -o $(DSTROOT)$(TEMPWRAPPER)
146	@set -x && \
147	for w in `sort -u $(OBJROOT)/wrappers`; do \
148	    ln -f $(DSTROOT)$(TEMPWRAPPER) $(DSTROOT)$(MERGEBIN)/$$w || exit 1; \
149	done
150	rm -f $(DSTROOT)$(TEMPWRAPPER)
151	cd $(DSTROOT)$(MERGEBIN) && ls | sort > $(DSTROOT)$(VERSIONBINLIST)
152
153DUMMY = dummy.py
154$(OBJROOT)/wrappers:
155	install -d $(DSTROOT)$(MERGEBIN)
156	install $(FIX)/$(DUMMY) $(DSTROOT)$(MERGEBIN)
157	@set -x && \
158	touch $(OBJROOT)/wrappers && \
159	for vers in $(ORDEREDVERS); do \
160	    pbin=$(PYFRAMEWORKVERSIONS)/$$vers/bin && \
161	    cd $(DSTROOT)$$pbin && \
162	    if [ -e 2to3 ]; then \
163		mv 2to3 2to3$$vers && \
164		ln -s 2to3$$vers 2to3 && \
165		sed -e 's/@SEP@//g' -e "s/@VERSION@/$$vers/g" $(FIX)/scriptvers.ed | ed - 2to3$$vers; \
166	    fi && \
167	    for f in `find . -type f | sed 's,^\./,,'`; do \
168		f0=`echo $$f | sed "s/$$vers//"` && \
169		ln -sf ../..$$pbin/$$f $(DSTROOT)$(MERGEBIN)/$$f && \
170		if file $$f | head -1 | fgrep -q script; then \
171		    sed -e 's/@SEP@//g' -e "s/@VERSION@/$$vers/g" $(FIX)/scriptvers.ed | ed - $$f && \
172		    if [ ! -e $(DSTROOT)$(MERGEBIN)/$$f0 ]; then \
173			ln -f $(DSTROOT)$(MERGEBIN)/$(DUMMY) $(DSTROOT)$(MERGEBIN)/$$f0; \
174		    fi; \
175		else \
176		    echo $$f0 >> $@; \
177		fi || exit 1; \
178	    done || exit 1; \
179	done
180	rm -f $(DSTROOT)$(MERGEBIN)/$(DUMMY)
181
182$(DSTROOT)$(VERSIONHEADER):
183	@set -x && ( \
184	    echo '#define DEFAULTVERSION "$(DEFAULT)"' && \
185	    echo '#define NVERSIONS (sizeof(versions) / sizeof(const char *))' && \
186	    echo '#define PROJECT "$(Project)"' && \
187	    printf '#define UPROJECT "%s"\n' `echo $(Project) | tr a-z A-Z` && \
188	    echo 'static const char *versions[] = {' && \
189	    touch $(OBJROOT)/versions && \
190	    for vers in $(VERSIONS); do \
191		echo $$vers >> $(OBJROOT)/versions || exit 1; \
192	    done && \
193	    for vers in `sort -u $(OBJROOT)/versions`; do \
194		printf '    "%s",\n' $$vers || exit 1; \
195	    done && \
196	    echo '};' ) > $@
197
198MERGEDEFAULT = \
199    usr/local/OpenSourceLicenses
200mergedefault:
201	cd $(OBJROOT)/$(DEFAULT)/DSTROOT && rsync -Ra $(MERGEDEFAULT) $(DSTROOT)
202
203MERGEMAN = /usr/share/man
204mergeman: domergeman customman listman
205
206# When merging man pages from the multiple versions, allow the man pages
207# to be compressed (.gz suffix) or not.
208domergeman:
209	@set -x && \
210	for vers in $(ORDEREDVERS); do \
211	    cd $(OBJROOT)/$$vers/DSTROOT$(MERGEMAN) && \
212	    for d in man*; do \
213		cd $$d && \
214		for f in `find . -type f -name '*.*' | sed 's,^\./,,'`; do \
215		    ff=`echo $$f | sed -E "s/\.[^.]*(.gz)?$$/$$vers&/"` && \
216		    ditto $$f $(DSTROOT)$(MERGEMAN)/$$d/$$ff && \
217		    if [ ! -e $(DSTROOT)$(MERGEMAN)/$$d/$$f ]; then \
218			ln -fs $$ff $(DSTROOT)$(MERGEMAN)/$$d/$$f; \
219		    fi || exit 1; \
220		done && \
221		cd .. || exit 1; \
222	    done || exit 1; \
223	done
224
225# When adding custom python.1 and pythonw.1 man pages, autodetect if we are
226# compressing man pages, and if so, compress these custom man pages as well
227CUSTOMTEMP = .temp.1
228customman: $(OBJROOT)/wrappers
229	@set -x && \
230	cp -f $(FIX)/$(Project).1 $(DSTROOT)$(MERGEMAN)/man1/$(CUSTOMTEMP) && \
231	cd $(DSTROOT)$(MERGEMAN)/man1 && \
232	suffix='' && \
233	if ls | grep -q '\.gz$$'; then suffix='.gz'; fi && \
234	if [ "$${suffix}" ]; then gzip $(CUSTOMTEMP); fi && \
235	for w in `sort -u $(OBJROOT)/wrappers`; do \
236	    rm -f $${w}.1$${suffix} && \
237	    ln -f $(CUSTOMTEMP)$${suffix} $${w}.1$${suffix} || exit 1; \
238	done && \
239	rm -f $(CUSTOMTEMP)$${suffix}
240
241listman:
242	cd $(DSTROOT)$(MERGEMAN) && find . ! -type d | sed 's,^\./,,' | sort > $(DSTROOT)$(VERSIONMANLIST)
243
244OPENSOURCEVERSIONS = /usr/local/OpenSourceVersions
245PLIST = $(OPENSOURCEVERSIONS)/$(Project).plist
246mergeplist:
247	mkdir -p $(DSTROOT)/$(OPENSOURCEVERSIONS)
248	echo '<plist version="1.0">' > $(DSTROOT)/$(PLIST)
249	echo '<array>' >> $(DSTROOT)/$(PLIST)
250	@set -x && \
251	for vers in $(VERSIONS); do \
252	    sed -e '/^<\/*plist/d' -e '/^<\/*array/d' -e 's/^/	/' $(OBJROOT)/$$vers/DSTROOT/$(PLIST) >> $(DSTROOT)/$(PLIST) || exit 1; \
253	done
254	echo '</array>' >> $(DSTROOT)/$(PLIST)
255	echo '</plist>' >> $(DSTROOT)/$(PLIST)
256	chmod 644 $(DSTROOT)/$(PLIST)
257
258MERGEVERSIONSCONDITIONAL = \
259    Developer/Applications
260MERGEVERSIONS = \
261    Library \
262    usr/include
263MERGEREVERSEVERSIONS = \
264    System
265mergeversions:
266	@set -x && \
267	for vers in $(VERSIONS); do \
268	    cd $(OBJROOT)/$$vers/DSTROOT && \
269	    rsync -Ra $(MERGEVERSIONS) $(DSTROOT) && \
270	    for c in $(MERGEVERSIONSCONDITIONAL); do \
271		if [ -e "$$c" ]; then \
272		    rsync -Ra "$$c" $(DSTROOT); \
273		fi || exit 1; \
274	    done || exit 1; \
275	done
276	for vers in $(REVERSEVERS); do \
277	    cd $(OBJROOT)/$$vers/DSTROOT && \
278	    rsync -Ra $(MERGEREVERSEVERSIONS) $(DSTROOT) || exit 1; \
279	done
280
281fixsmptd:
282	set -x && \
283	cd $(DSTROOT)/usr/bin && \
284	mv -f smtpd.py smtpd.py.bak && \
285	cp -pf smtpd.py.bak smtpd.py && \
286	rm -f smtpd.py.bak && \
287	for i in smtpd*.py; do \
288	    ed - $$i < $(FIX)/smtpd.py.ed || exit 1; \
289	done
290