source: trunk/dports/_resources/port1.0/group/muniversal-1.0.tcl @ 125633

Last change on this file since 125633 was 115752, checked in by jeremyhu@…, 6 years ago

muniversal: --host argument does not need to match deployment target.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.1 KB
Line 
1# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4
2# $Id: muniversal-1.0.tcl 115752 2014-01-10 20:33:33Z jeremyhu@macports.org $
3#
4# Copyright (c) 2009-2013 The MacPorts Project,
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
10#
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16# 3. Neither the name of Apple Computer, Inc. nor the names of its
17#    contributors may be used to endorse or promote products derived from
18#    this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#
32
33# User variables:
34#         merger_configure_env: associative array of configure.env variables
35#             merger_build_env: associative array of build.env variables
36#          merger_destroot_env: associative array of destroot.env variables
37#                  merger_host: associative array of host values
38#        merger_configure_args: associative array of configure.args
39#            merger_build_args: associative array of build.args
40#         merger_destroot_args: associative array of destroot.args
41#    merger_configure_compiler: associative array of configure.compiler
42#    merger_configure_cppflags: associative array of configure.cppflags
43#      merger_configure_cflags: associative array of configure.cflags
44#    merger_configure_cxxflags: associative array of configure.cxxflags
45#   merger_configure_objcflags: associative array of configure.objcflags
46#     merger_configure_ldflags: associative array of configure.ldflags
47#             merger_arch_flag: if no, -arch xxx will not be appended configure.???flags
48#         merger_arch_compiler: if no, -arch xxx will not be appended to compilers
49#             merger_dont_diff: list of file names for which diff will not work
50#     merger_must_run_binaries: if yes, build platform must be able to run binaries for supported architectures
51#            merger_no_3_archs: if yes, merger will not work correctly if there are three supported architectures
52
53options universal_archs_supported merger_must_run_binaries merger_no_3_archs merger_arch_flag merger_arch_compiler
54default universal_archs_supported {${universal_archs}}
55default merger_must_run_binaries {no}
56default merger_no_3_archs {no}
57default merger_arch_flag {yes}
58default merger_arch_compiler {no}
59
60proc muniversal_arch_flag_supported {args} {
61    global configure.compiler
62    return [regexp {^gcc-4|llvm|apple|clang} ${configure.compiler}]
63}
64
65proc muniversal_get_arch_flag {arch {fortran ""}} {
66    global os.arch
67    # Prefer -arch to -m
68    if {[muniversal_arch_flag_supported] && ${fortran}==""} {
69        set archf "-arch ${arch}"
70    } else {
71        if { ${os.arch}=="i386" && ${arch}=="i386" } {
72            set archf -m32
73        } elseif { ${os.arch}=="i386" && ${arch}=="x86_64" } {
74            set archf -m64
75        } elseif { ${os.arch}=="powerpc" && ${arch}=="ppc" } {
76            set archf -m32
77        } elseif { ${os.arch}=="powerpc" && ${arch}=="ppc64" } {
78            set archf -m64
79        } else {
80            if { ${fortran}=="" } {
81                return -code error "selected compiler can't build for ${arch}"
82            } else {
83                return ""
84            }
85        }
86    }
87    return ${archf}
88}
89
90# set up the merger-post-destroot hook
91
92proc merger_target_provides {ditem args} {
93    global targets
94    # register just the procedure, no pre-/post-
95    # User-code exceptions are caught and returned as a result of the target.
96    # Thus if the user code breaks, dependent targets will not execute.
97    foreach target $args {
98        set origproc [ditem_key $ditem procedure]
99        set ident [ditem_key $ditem name]
100        proc merger-post-$target {args} "
101            variable proc_index
102            set proc_index \[llength \[ditem_key $ditem post\]\]
103            ditem_append $ditem merger-post proc-merger-post-${ident}-${target}-\${proc_index}
104            proc proc-merger-post-${ident}-${target}-\${proc_index} {name} \"
105                if {\\\[catch userproc-merger-post-${ident}-${target}-\${proc_index} result\\\]} {
106                    return -code error \\\$result
107                } else {
108                    return 0
109                }
110            \"
111            makeuserproc userproc-merger-post-${ident}-${target}-\${proc_index} \$args
112        "
113    }
114}
115
116merger_target_provides ${org.macports.destroot} destroot
117
118variant universal {
119    global universal_archs_to_use
120
121    foreach arch ${universal_archs} {
122        configure.universal_cflags-delete    -arch ${arch}
123        configure.universal_cxxflags-delete  -arch ${arch}
124        configure.universal_ldflags-delete   -arch ${arch}
125    }
126
127    eval configure.args-append      ${configure.universal_args}
128    eval configure.cflags-append    ${configure.universal_cflags}
129    eval configure.cxxflags-append  ${configure.universal_cxxflags}
130    eval configure.objcflags-append ${configure.universal_cflags}
131    eval configure.ldflags-append   ${configure.universal_ldflags}
132    eval configure.cppflags-append  ${configure.universal_cppflags}
133
134    # user has specified that build platform must be able to run binaries for supported architectures
135    if { ${merger_must_run_binaries}=="yes" } {
136        if { ${os.arch}=="i386" } {
137            set universal_archs_supported [ldelete ${universal_archs_supported} "ppc64"]
138            if {${os.major} >= 9 && [sysctl hw.cpu64bit_capable] == 0} {
139                set universal_archs_supported [ldelete ${universal_archs_supported} "x86_64"]
140            }
141        } else {
142            set universal_archs_supported [ldelete ${universal_archs_supported} "i386"]
143            set universal_archs_supported [ldelete ${universal_archs_supported} "x86_64"]
144            if {${os.major} >= 9 && [sysctl hw.cpu64bit_capable] == 0} {
145                set universal_archs_supported [ldelete ${universal_archs_supported} "ppc64"]
146            }
147        }
148    }
149
150    # set universal_archs_to_use as the intersection of universal_archs and universal_archs_supported
151    set universal_archs_to_use {}
152    foreach arch ${universal_archs} {
153        set arch_ok no
154        foreach archt ${universal_archs_supported} {
155            if { ${arch}==${archt} } {
156                set arch_ok yes
157            }
158        }
159        if { ${arch_ok}=="yes" } {
160            lappend universal_archs_to_use ${arch}
161        }
162    }
163
164    # if merger_no_3_archs is yes, prune universal_archs_to_use until it only has two elements
165    if { ${merger_no_3_archs}=="yes" } {
166        if { [llength ${universal_archs_to_use}] == 3 } {
167            # first try to remove cross-compiled 64-bit arch
168            if { ${os.arch}=="i386" } {
169                set universal_archs_to_use [ldelete ${universal_archs_to_use} "ppc64"]
170            } else {
171                set universal_archs_to_use [ldelete ${universal_archs_to_use} "x86_64"]
172            }
173        }
174        if { [llength ${universal_archs_to_use}] == 3 } {
175            # next try to remove cross-compiled 32-bit arch
176            if { ${os.arch}=="i386" } {
177                set universal_archs_to_use [ldelete ${universal_archs_to_use} "ppc"]
178            } else {
179                set universal_archs_to_use [ldelete ${universal_archs_to_use} "i386"]
180            }
181        }
182        if { [llength ${universal_archs_to_use}] == 3 } {
183            # at least one arch should have been removed from universal_archs_to_use
184            error "Should Not Happen"
185        }
186    }
187
188    configure {
189        # Fix inability to find nm when cross-compiling (#22224, #23431, #23687, #24477, et al)
190        configure.env-append    NM=/usr/bin/nm
191
192        foreach arch ${universal_archs_to_use} {
193            ui_info "$UI_PREFIX [format [msgcat::mc "Configuring %1\$s for architecture %2\$s"] ${subport} ${arch}]"
194
195            if {![file exists ${worksrcpath}-${arch}]} {
196                copy ${worksrcpath} ${worksrcpath}-${arch}
197            }
198
199            set archf [muniversal_get_arch_flag ${arch}]
200            set archff [muniversal_get_arch_flag ${arch} "fortran"]
201
202            if { ${merger_arch_flag} != "no" } {
203                configure.cflags-append    ${archf}
204                configure.cxxflags-append  ${archf}
205                configure.objcflags-append ${archf}
206                configure.fflags-append    ${archff}
207                configure.fcflags-append   ${archff}
208                configure.f90flags-append  ${archff}
209                configure.ldflags-append   ${archf}
210            }
211
212            if { [info exists merger_configure_env(${arch})] } {
213                configure.env-append  $merger_configure_env(${arch})
214            }
215            if { [info exists merger_configure_cppflags(${arch})] } {
216                configure.cppflags-append  $merger_configure_cppflags(${arch})
217            }
218            if { [info exists merger_configure_cflags(${arch})] } {
219                configure.cflags-append  $merger_configure_cflags(${arch})
220            }
221            if { [info exists merger_configure_cxxflags(${arch})] } {
222                configure.cxxflags-append  $merger_configure_cxxflags(${arch})
223            }
224            if { [info exists merger_configure_objcflags(${arch})] } {
225                configure.objcflags-append  $merger_configure_objcflags(${arch})
226            }
227            if { [info exists merger_configure_ldflags(${arch})] } {
228                configure.ldflags-append  $merger_configure_ldflags(${arch})
229            }
230
231            # Don't set the --host unless we have to.
232            set host ""
233            if { [info exists merger_host($arch)] } {
234                if { $merger_host($arch) != "" } {
235                    set host  --host=$merger_host($arch)
236                }
237            } elseif {[file tail ${configure.cmd}] != "cmake"} {
238                # check if building for a word length we can't run
239                set bits_differ 0
240                if {(${arch}=="x86_64" || ${arch}=="ppc64") &&
241                    (${os.major} < 9 || [sysctl hw.cpu64bit_capable] == 0)} {
242                    set bits_differ 1
243                }
244                # check if building for a completely different arch
245                if {$bits_differ || (${os.arch}=="i386" && (${arch}=="ppc" || ${arch}=="ppc64"))
246                        || (${os.arch}=="powerpc" && (${arch}=="i386" || ${arch}=="x86_64"))} {
247                    switch -- ${arch} {
248                        x86_64  {set host "--host=x86_64-apple-${os.platform}${os.version}"}
249                        i386    {set host "--host=i686-apple-${os.platform}${os.version}"}
250                        ppc     {set host "--host=powerpc-apple-${os.platform}${os.version}"}
251                        ppc64   {set host "--host=powerpc64-apple-${os.platform}${os.version}"}
252                    }
253                }
254            }
255            if {$host != ""} {
256                configure.args-append  ${host}
257            }
258
259            if { [info exists merger_configure_args(${arch})] } {
260                configure.args-append  $merger_configure_args(${arch})
261            }
262
263            set configure_compiler_save ${configure.compiler}
264            set configure_cc_save       ${configure.cc}
265            set configure_cxx_save      ${configure.cxx}
266            set configure_objc_save     ${configure.objc}
267            set configure_fc_save       ${configure.fc}
268            set configure_f77_save      ${configure.f77}
269            set configure_f90_save      ${configure.f90}
270
271            if { [info exists merger_configure_compiler($arch)] } {
272                configure.compiler  $merger_configure_compiler($arch)
273                configure.cc        [portconfigure::configure_get_compiler cc]
274                configure.cxx       [portconfigure::configure_get_compiler cxx]
275                configure.objc      [portconfigure::configure_get_compiler objc]
276                configure.f77       [portconfigure::configure_get_compiler f77]
277                configure.f90       [portconfigure::configure_get_compiler f90]
278                configure.fc        [portconfigure::configure_get_compiler fc]
279            }
280
281            if { ${merger_arch_compiler} != "no" } {
282                configure.cc   ${configure.cc}   ${archf}
283                configure.cxx  ${configure.cxx}  ${archf}
284                configure.objc ${configure.objc} ${archf}
285                if { ${configure.fc}  != "" } { configure.fc   ${configure.fc}  ${archff} }
286                if { ${configure.f77} != "" } { configure.f77  ${configure.f77} ${archff} }
287                if { ${configure.f90} != "" } { configure.f90  ${configure.f90} ${archff} }
288            }
289
290            set configure_dir_save  ${configure.dir}
291            if { [string match "${worksrcpath}/*" ${configure.dir}] } {
292                # The configure directory is inside the source directory, so put in the new source directory name.
293                eval configure.dir  [string map "${worksrcpath} ${worksrcpath}-${arch}" ${configure.dir}]
294            } else {
295                # The configure directory is outside the source directory, so give it a new name by appending ${arch}.
296                configure.dir  ${configure.dir}-${arch}
297                if { ![file exists ${configure.dir}] } {
298                    file mkdir ${configure.dir}
299                }
300            }
301
302            set autoreconf_dir_save  ${autoreconf.dir}
303            if { [string match "${worksrcpath}/*" ${autoreconf.dir}] } {
304                # The autoreconf directory is inside the source directory, so put in the new source directory name.
305                eval autoreconf.dir  [string map "${worksrcpath} ${worksrcpath}-${arch}" ${autoreconf.dir}]
306            } else {
307                # The autoreconf directory is outside the source directory, so give it a new name by appending ${arch}.
308                autoreconf.dir  ${autoreconf.dir}-${arch}
309                if { ![file exists ${autoreconf.dir}] } {
310                    file mkdir ${autoreconf.dir}
311                }
312            }
313
314            portconfigure::configure_main
315
316            # Undo changes to the configure related variables
317            eval autoreconf.dir ${autoreconf_dir_save}
318            eval configure.dir  ${configure_dir_save}
319            eval configure.compiler ${configure_compiler_save}
320            eval configure.f90  ${configure_f90_save}
321            eval configure.f77  ${configure_f77_save}
322            eval configure.fc   ${configure_fc_save}
323            eval configure.cc   ${configure_cc_save}
324            eval configure.cxx  ${configure_cxx_save}
325            eval configure.objc ${configure_objc_save}
326            if { [info exists merger_configure_args(${arch})] } {
327                configure.args-delete  $merger_configure_args(${arch})
328            }
329            configure.args-delete  ${host}
330            if { [info exists merger_configure_ldflags(${arch})] } {
331                configure.ldflags-delete  $merger_configure_ldflags(${arch})
332            }
333            if { [info exists merger_configure_cxxflags(${arch})] } {
334                configure.cxxflags-delete  $merger_configure_cxxflags(${arch})
335            }
336            if { [info exists merger_configure_objcflags(${arch})] } {
337                configure.objcflags-delete  $merger_configure_objcflags(${arch})
338            }
339            if { [info exists merger_configure_cflags(${arch})] } {
340                configure.cflags-delete  $merger_configure_cflags(${arch})
341            }
342            if { [info exists merger_configure_cppflags(${arch})] } {
343                configure.cppflags-delete  $merger_configure_cppflags(${arch})
344            }
345            if { [info exists merger_configure_env(${arch})] } {
346                configure.env-delete  $merger_configure_env(${arch})
347            }
348            if { ${merger_arch_flag} != "no" } {
349                configure.ldflags-delete   ${archf}
350                configure.f90flags-delete  ${archff}
351                configure.fcflags-delete   ${archff}
352                configure.fflags-delete    ${archff}
353                configure.objcflags-delete ${archf}
354                configure.cxxflags-delete  ${archf}
355                configure.cflags-delete    ${archf}
356            }
357        }
358    }
359
360    build {
361        foreach arch ${universal_archs_to_use} {
362            ui_info "$UI_PREFIX [format [msgcat::mc "Building %1\$s for architecture %2\$s"] ${subport} ${arch}]"
363
364            if { [info exists merger_build_env(${arch})] } {
365                build.env-append  $merger_build_env(${arch})
366            }
367            if { [info exists merger_build_args(${arch})] } {
368                build.args-append  $merger_build_args(${arch})
369            }
370            set build_dir_save  ${build.dir}
371            if { [string match "${worksrcpath}/*" ${build.dir}] } {
372                # The build directory is inside the source directory, so put in the new source directory name.
373                eval build.dir  [string map "${worksrcpath} ${worksrcpath}-${arch}" ${build.dir}]
374            } else {
375                # The build directory is outside the source directory, so give it a new name by appending ${arch}.
376                build.dir  ${build.dir}-${arch}
377                if { ![file exists ${build.dir}] } {
378                    file mkdir ${build.dir}
379                }
380            }
381
382            portbuild::build_main
383
384            eval build.dir  ${build_dir_save}
385            if { [info exists merger_build_args(${arch})] } {
386                build.args-delete $merger_build_args(${arch})
387            }
388            if { [info exists merger_build_env(${arch})] } {
389                build.env-delete  $merger_build_env(${arch})
390            }
391        }
392    }
393
394    destroot {
395        foreach arch ${universal_archs_to_use} {
396            ui_info "$UI_PREFIX [format [msgcat::mc "Staging %1\$s into destroot for architecture %2\$s"] ${subport} ${arch}]"
397            copy ${destroot} ${workpath}/destroot-${arch}
398            set destdirSave ${destroot.destdir}
399            eval destroot.destdir  [string map "${destroot} ${workpath}/destroot-${arch}" ${destroot.destdir}]
400
401            if { [info exists merger_destroot_env(${arch})] } {
402                destroot.env-append  $merger_destroot_env(${arch})
403            }
404            if { [info exists merger_destroot_args(${arch})] } {
405                destroot.args-append  $merger_destroot_args(${arch})
406            }
407            set destroot_dir_save ${destroot.dir}
408            if { [string match "${worksrcpath}/*" ${destroot.dir}] } {
409                # The destroot directory is inside the source directory, so put in the new source directory name.
410                eval destroot.dir  [string map "${worksrcpath} ${worksrcpath}-${arch}" ${destroot.dir}]
411            } else {
412                # The destroot directory is outside the source directory, so give it a new name by appending ${arch}.
413                destroot.dir  ${destroot.dir}-${arch}
414                if { ![file exists ${destroot.dir}] } {
415                    file mkdir ${destroot.dir}
416                }
417            }
418
419            portdestroot::destroot_main
420
421            destroot.dir  ${destroot_dir_save}
422            if { [info exists merger_destroot_args(${arch})] } {
423                destroot.args-delete $merger_destroot_args(${arch})
424            }
425            if { [info exists merger_destroot_env(${arch})] } {
426                destroot.env-delete  $merger_destroot_env(${arch})
427            }
428            eval destroot.destdir ${destdirSave}
429        }
430        delete ${destroot}
431
432        # execute merger-post-destroot, if it exists
433
434        set ditem ${org.macports.destroot}
435        set procedure [ditem_key $ditem merger-post]
436        if {$procedure != ""} {
437            set targetname [ditem_key $ditem name]
438            ui_debug "Executing org.macports.merger-post-destroot"
439            set result [catch { $procedure $targetname } errstr]
440            # Save variables in order to re-throw the same error code.
441            set errcode $::errorCode
442            set errinfo $::errorInfo
443            if {$result != 0} {
444                set portname $subport
445                ui_error "$targetname for port $portname returned: $errstr"
446                ui_debug "Error code: $errcode"
447                ui_debug "Backtrace: $errinfo"
448                return $result
449            }
450        }
451
452        # Merge two files (${dir1}/${fl} and ${dir2}/${fl}) to ${dir}/${fl}
453        # by stripping out -arch XXXX, -m32, and -m64
454        proc mergeStripArchFlags {dir1 dir2 dir fl} {
455            set tempdir [mkdtemp "/tmp/muniversal.XXXXXXXX"]
456            set tempfile1 "${tempdir}/1-${fl}"
457            set tempfile2 "${tempdir}/2-${fl}"
458
459            copy ${dir1}/${fl} ${tempfile1}
460            copy ${dir2}/${fl} ${tempfile2}
461
462            reinplace -E {s:-arch +[^ ]+::g} ${tempfile1} ${tempfile2}
463            reinplace {s:-m32::g} ${tempfile1} ${tempfile2}
464            reinplace {s:-m64::g} ${tempfile1} ${tempfile2}
465
466            if { ! [catch {system "/usr/bin/cmp -s \"${tempfile1}\" \"${tempfile2}\""}] } {
467                # modified files are identical
468                ui_debug "universal: merge: ${fl} differs in ${dir1} and ${dir2} but are the same when stripping out -m32, -m64, and -arch XXX"
469                copy ${tempfile1} ${dir}/${fl}
470                delete ${tempfile1} ${tempfile2} ${tempdir}
471            } else {
472                delete ${tempfile1} ${tempfile2} ${tempdir}
473                return -code error "${fl} differs in ${dir1} and ${dir2} and cannot be merged"
474            }
475        }
476
477        # Merge ${base1}/${prefixDir} and ${base2}/${prefixDir} into dir ${base}/${prefixDir}
478        #        arch1, arch2: names to prepend to files if a diff merge of two files is forbidden by merger_dont_diff
479        #    merger_dont_diff: list of files for which /usr/bin/diff ${diffFormat} will not merge correctly
480        #          diffFormat: format used by diff to merge two text files
481        proc merge2Dir {base1 base2 base prefixDir arch1 arch2 merger_dont_diff diffFormat} {
482            set dir1  ${base1}/${prefixDir}
483            set dir2  ${base2}/${prefixDir}
484            set dir   ${base}/${prefixDir}
485
486            xinstall -d -m 0755 ${dir}
487
488            foreach fl [glob -directory ${dir2} -tails -nocomplain * .*] {
489                if { ${fl} == "." || ${fl} == ".." } {
490                    continue
491                }
492                if { ![muniversal_file_or_symlink_exists ${dir1}/${fl}] } {
493                    # File only exists in ${dir1}
494                    ui_debug "universal: merge: ${prefixDir}/${fl} only exists in ${base2}"
495                    copy ${dir2}/${fl} ${dir}
496                }
497            }
498            foreach fl [glob -directory ${dir1} -tails -nocomplain * .*] {
499                if { ${fl} == "." || ${fl} == ".." } {
500                    continue
501                }
502                if { ![muniversal_file_or_symlink_exists ${dir2}/${fl}] } {
503                    # File only exists in ${dir2}
504                    ui_debug "universal: merge: ${prefixDir}/${fl} only exists in ${base1}"
505                    copy ${dir1}/${fl} ${dir}
506                } else {
507                    # File exists in ${dir1} and ${dir2}
508                    ui_debug "universal: merge: merging ${prefixDir}/${fl} from ${base1} and ${base2}"
509
510                    # Ensure files are of same type
511                    if { [file type ${dir1}/${fl}]!=[file type ${dir2}/${fl}] } {
512                        error "${dir1}/${fl} and ${dir2}/${fl} are of different types"
513                    }
514
515                    if { [file type ${dir1}/${fl}]=="link" } {
516                        # Files are links
517                        ui_debug "universal: merge: ${prefixDir}/${fl} is a link"
518
519                        # Ensure links don't point to different things
520                        if { [file readlink ${dir1}/${fl}]==[file readlink ${dir2}/${fl}] } {
521                            copy ${dir1}/${fl} ${dir}
522                        } else {
523                            error "${dir1}/${fl} and ${dir2}/${fl} point to different targets (can't merge them)"
524                        }
525                    } elseif { [file isdirectory ${dir1}/${fl}] } {
526                        # Files are directories (but not links), so recursively call function
527                        merge2Dir ${base1} ${base2} ${base} ${prefixDir}/${fl} ${arch1} ${arch2} ${merger_dont_diff} ${diffFormat}
528                    } else {
529                        # Files are neither directories nor links
530                        if { ! [catch {system "/usr/bin/cmp -s \"${dir1}/${fl}\" \"${dir2}/${fl}\" && /bin/cp -v \"${dir1}/${fl}\" \"${dir}\""}] } {
531                            # Files are byte by byte the same
532                            ui_debug "universal: merge: ${prefixDir}/${fl} is identical in ${base1} and ${base2}"
533                        } else {
534                            # Actually try to merge the files
535                            # First try lipo, then libtool
536                            if { ! [catch {system "/usr/bin/lipo -create \"${dir1}/${fl}\" \"${dir2}/${fl}\" -output \"${dir}/${fl}\""}] } {
537                                # lipo worked
538                                ui_debug "universal: merge: lipo created ${prefixDir}/${fl}"
539                            } elseif { ! [catch {system "/usr/bin/libtool \"${dir1}/${fl}\" \"${dir2}/${fl}\" -o \"${dir}/${fl}\""}] } {
540                                # libtool worked
541                                ui_debug "universal: merge: libtool created ${prefixDir}/${fl}"
542                            } else {
543                                # lipo and libtool have failed, so assume they are text files to be merged
544                                set dontdiff no
545                                foreach dont ${merger_dont_diff} {
546                                    if { ${dont}=="${prefixDir}/${fl}" } {
547                                        set dontdiff yes
548                                    }
549                                }
550                                if { ${dontdiff}==yes } {
551                                    # user has specified that diff does not work
552                                    # attempt to give each file a unique name and create a new file which includes one of the original depending on the arch
553
554                                    set fh [open ${dir}/${arch1}-${fl} w 0644]
555                                    puts ${fh} "#include \"${arch1}-${fl}\""
556                                    close ${fh}
557
558                                    set fh [open ${dir}/${arch2}-${fl} w 0644]
559                                    puts ${fh} "#include \"${arch2}-${fl}\""
560                                    close ${fh}
561
562                                    ui_debug "universal: merge: created ${prefixDir}/${fl} to include ${prefixDir}/${arch1}-${fl} ${prefixDir}/${arch1}-${fl}"
563
564                                    system "/usr/bin/diff -d ${diffFormat} \"${dir}/${arch1}-${fl}\" \"${dir}/${arch2}-${fl}\" > \"${dir}/${fl}\"; test \$? -le 1"
565
566                                    copy -force ${dir1}/${fl} ${dir}/${arch1}-${fl}
567                                    copy -force ${dir2}/${fl} ${dir}/${arch2}-${fl}
568                                } else {
569                                    # Text file on which diff will not give correct results.
570                                    switch -glob ${fl} {
571                                        *.mod {
572                                            # .mod files from Fortran modules.
573                                            # Create a sepcial module directory for each architecture.
574                                            # To find these modules, GFortran might require -M or -J.
575                                            file mkdir ${dir}/mods32
576                                            file mkdir ${dir}/mods64
577                                            if { ${arch1}=="i386" || ${arch1}=="ppc" } {
578                                                copy ${dir1}/${fl} ${dir}/mods32
579                                                copy ${dir2}/${fl} ${dir}/mods64
580                                            } else {
581                                                copy ${dir2}/${fl} ${dir}/mods32
582                                                copy ${dir1}/${fl} ${dir}/mods64
583                                            }
584                                        }
585                                        *.pc -
586                                        *-config {
587                                            mergeStripArchFlags ${dir1} ${dir2} ${dir} ${fl}
588                                        }
589                                        *.la {
590                                            global destroot.delete_la_files
591                                            if {${destroot.delete_la_files} == "yes"} {
592                                                ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2}; ignoring due to delete_la_files"
593                                            } else {
594                                                return -code error "${prefixDir}/${fl} differs in ${base1} and ${base2} and cannot be merged"
595                                            }
596                                        }
597                                        *.typelib {
598                                            # Sometimes garbage ends up in ignored trailing bytes
599                                            # https://trac.macports.org/ticket/39629
600                                            # TODO: Compare the g-ir-generate output
601                                            ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2}; assume trivial difference"
602                                            copy ${dir1}/${fl} ${dir}
603                                        }
604                                        *.jar {
605                                            # jar files can be different because of timestamp
606                                            ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2}; assume timestamp difference"
607                                            copy ${dir1}/${fl} ${dir}
608                                        }
609                                        *.elc {
610                                            # elc files can be different because they record when and where they were built.
611                                            ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2}; assume trivial difference"
612                                            copy ${dir1}/${fl} ${dir}
613                                        }
614                                        *.el.gz -
615                                        *.el.bz2 {
616                                            # Emacs lisp files should be same across architectures
617                                            # the emacs package (and perhaps others) records the date of automatically generated el files
618                                            ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2}; assume trivial difference"
619                                            copy ${dir1}/${fl} ${dir}
620                                        }
621                                        *.lzma -
622                                        *.xz -
623                                        *.gz -
624                                        *.bz2 {
625                                            # compressed files can differ due to entropy
626                                            switch -glob ${fl} {
627                                                *.lzma {
628                                                    set cat ${prefix}/bin/lzcat
629                                                }
630                                                *.xz {
631                                                    set cat ${prefix}/bin/xzcat
632                                                }
633                                                *.gz {
634                                                    set cat /usr/bin/gzcat
635                                                }
636                                                *.bz2 {
637                                                    set cat /usr/bin/bzcat
638                                                }
639                                            }
640                                            set tempdir [mkdtemp "/tmp/muniversal.XXXXXXXX"]
641                                            set tempfile1 "${tempdir}/${arch1}-[file rootname ${fl}]"
642                                            set tempfile2 "${tempdir}/${arch2}-[file rootname ${fl}]"
643                                            system "${cat} \"${dir1}/${fl}\" > \"${tempfile1}\""
644                                            system "${cat} \"${dir2}/${fl}\" > \"${tempfile2}\""
645                                            if { ! [catch {system "/usr/bin/cmp -s \"${tempfile1}\" \"${tempfile2}\""}] } {
646                                                # files are identical
647                                                ui_debug "universal: merge: ${prefixDir}/${fl} differs in ${base1} and ${base2} but the contents are the same"
648                                                copy ${dir1}/${fl} ${dir}
649                                                delete ${tempfile1} ${tempfile2} ${tempdir}
650                                            } else {
651                                                delete ${tempfile1} ${tempfile2} ${tempdir}
652                                                return -code error "${prefixDir}/${fl} differs in ${base1} and ${base2} and cannot be merged"
653                                            }
654                                        }
655                                        default {
656                                            if { ! [catch {system "test `head -c2 ${dir1}/${fl}` == '#!'"}] } {
657                                                # Shell script, hopefully striping out arch flags works...
658                                                mergeStripArchFlags ${dir1} ${dir2} ${dir} ${fl}
659                                            } elseif { ! [catch {system "/usr/bin/diff -dw ${diffFormat} \"${dir1}/${fl}\" \"${dir2}/${fl}\" > \"${dir}/${fl}\"; test \$? -le 1"} ] } {
660                                                # diff worked
661                                                ui_debug "universal: merge: used diff to create ${prefixDir}/${fl}"
662                                            } else {
663                                                # File created by diff is invalid
664                                                delete ${dir}/${fl}
665                                                return -code error "${prefixDir}/${fl} differs in ${base1} and ${base2} and cannot be merged"
666                                            }
667                                        }
668                                    }
669                                }
670                            }
671                        }
672                    }
673                }
674            }
675        }
676
677        # /usr/bin/diff can merge two C/C++ files
678        # See http://www.gnu.org/software/diffutils/manual/html_mono/diff.html#If-then-else
679        # See http://www.gnu.org/software/diffutils/manual/html_mono/diff.html#Detailed%20If-then-else
680        set diffFormatProc {--old-group-format='#if (defined(__ppc__) || defined(__ppc64__))
681 %<#endif
682' \
683--new-group-format='#if defined (__i386__) || defined(__x86_64__)
684%>#endif
685' \
686--unchanged-group-format='%=' \
687--changed-group-format='#if (defined(__ppc__) || defined(__ppc64__))
688%<#else
689%>#endif
690'}
691
692        set diffFormatM "-D __LP64__"
693
694        if { ![info exists merger_dont_diff] } {
695            set merger_dont_diff {}
696        }
697
698        merge2Dir  ${workpath}/destroot-ppc      ${workpath}/destroot-ppc64 ${workpath}/destroot-powerpc  ""  ppc ppc64    ${merger_dont_diff}  ${diffFormatM}
699        merge2Dir  ${workpath}/destroot-i386     ${workpath}/destroot-x86_64 ${workpath}/destroot-intel   ""  i386 x86_64  ${merger_dont_diff}  ${diffFormatM}
700        merge2Dir  ${workpath}/destroot-powerpc  ${workpath}/destroot-intel ${workpath}/destroot          ""  powerpc x86  ${merger_dont_diff}  ${diffFormatProc}
701    }
702
703    test {
704        foreach arch ${universal_archs_to_use} {
705            # Rosetta does not translate G5 instructions
706            # PowerPC systems can't translate Intel instructions
707            if { (${os.arch}=="i386" && ${arch}!="ppc64") || (${os.arch}=="powerpc" && ${arch}!="i386" && ${arch}!="x86_64") } {
708                ui_info "$UI_PREFIX [format [msgcat::mc "Testing %1\$s for architecture %2\$s"] ${subport} ${arch}]"
709                set test_dir_save ${test.dir}
710                if { [string match "${worksrcpath}/*" ${test.dir}] } {
711                    # The test directory is inside the source directory, so put in the new source directory name.
712                    eval test.dir  [string map "${worksrcpath} ${worksrcpath}-${arch}" ${test.dir}]
713                } else {
714                    # The test directory is outside the source directory, so give it a new name by appending ${arch}.
715                    test.dir  ${test.dir}-${arch}
716                    if { ![file exists ${test.dir}] } {
717                        file mkdir ${test.dir}
718                    }
719                }
720
721                porttest::test_main
722
723                test.dir ${test_dir_save}
724            }
725        }
726    }
727}
728
729# [muniversal_file_or_symlink_exists ${f}] tells you if ${f} exists. And unlike
730# [file exists ${f}], if used on a symlink, [muniversal_file_or_symlink_exists ${f}]
731# tells you about the symlink, not what it points to.
732proc muniversal_file_or_symlink_exists {f} {
733    # If [file type ${f}] throws an error, ${f} doesn't exist.
734    if {[catch {file type ${f}}]} {
735        return 0
736    }
737    # Otherwise, it does.
738    return 1
739}
Note: See TracBrowser for help on using the repository browser.