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