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

Last change on this file since 135550 was 135550, checked in by larryv@…, 4 years ago

muniversal-1.0: Set options with option proc (#47388)

A portfile command sets its associated option to a list composed of its
arguments. Accessing the associated global variable then yields that
list. Thus, for a portfile option foo,

foo 1 2 3
set bar $foo
foo $bar

does not restore foo to a list containing 1, 2, and 3. Instead, foo
is set to a list containing a list containing 1, 2, and 3. One way to
correct this is with eval:

foo 1 2 3
set bar $foo
eval foo $bar

The use of eval is generally discouraged because it's very easy to
create subtle injection bugs/vulnerabilities.[1] Tcl 8.5 argument
expansion permits an alternative:

foo 1 2 3
set bar $foo
foo {*}$bar

It's not particularly obvious when the double substitution[2] provided
by eval/{*} is required. This has caused much confusion in both base
and portfile code.

Another option (more palatable in base code) is to dispense with the
portfile command magic altogether and use the option proc directly.

option foo {1 2 3}
set bar [option foo]
option foo $bar

The option proc behaves more or less like set and is thus easier to
understand, if more verbose.

This is the approach I've taken to handling the bugs reported in #47388
and #47420. The muniversal-1.0 portgroup looks nothing like a portfile
at this point, so I think the use of portfile commands just muddles the
reader's mental model of what's going on. The simplicity of option is
preferable here.

[1] http://wiki.tcl.tk/1017
[2] http://wiki.tcl.tk/1535

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.4 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 135550 2015-04-26 02:54:46Z larryv@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    configure.args-append      {*}${configure.universal_args}
128    configure.cflags-append    {*}${configure.universal_cflags}
129    configure.cxxflags-append  {*}${configure.universal_cxxflags}
130    configure.objcflags-append {*}${configure.universal_cflags}
131    configure.ldflags-append   {*}${configure.universal_ldflags}
132    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                option 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                option 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                option 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                option 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            option autoreconf.dir       ${autoreconf_dir_save}
318            option configure.dir        ${configure_dir_save}
319            option configure.compiler   ${configure_compiler_save}
320            option configure.f90        ${configure_f90_save}
321            option configure.f77        ${configure_f77_save}
322            option configure.fc         ${configure_fc_save}
323            option configure.cc         ${configure_cc_save}
324            option configure.cxx        ${configure_cxx_save}
325            option 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                option 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                option 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            option 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            option 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                option 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                option 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            option 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            option 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 {--old-group-format='#ifndef __LP64__
693%<#endif
694' \
695--new-group-format='#ifdef __LP64__
696%>#endif
697' \
698--unchanged-group-format='%=' \
699--changed-group-format='#ifndef __LP64__
700%<#else
701%>#endif
702'}
703
704        if { ![info exists merger_dont_diff] } {
705            set merger_dont_diff {}
706        }
707
708        merge2Dir  ${workpath}/destroot-ppc      ${workpath}/destroot-ppc64 ${workpath}/destroot-powerpc  ""  ppc ppc64    ${merger_dont_diff}  ${diffFormatM}
709        merge2Dir  ${workpath}/destroot-i386     ${workpath}/destroot-x86_64 ${workpath}/destroot-intel   ""  i386 x86_64  ${merger_dont_diff}  ${diffFormatM}
710        merge2Dir  ${workpath}/destroot-powerpc  ${workpath}/destroot-intel ${workpath}/destroot          ""  powerpc x86  ${merger_dont_diff}  ${diffFormatProc}
711    }
712
713    test {
714        foreach arch ${universal_archs_to_use} {
715            # Rosetta does not translate G5 instructions
716            # PowerPC systems can't translate Intel instructions
717            if { (${os.arch}=="i386" && ${arch}!="ppc64") || (${os.arch}=="powerpc" && ${arch}!="i386" && ${arch}!="x86_64") } {
718                ui_info "$UI_PREFIX [format [msgcat::mc "Testing %1\$s for architecture %2\$s"] ${subport} ${arch}]"
719                set test_dir_save ${test.dir}
720                if { [string match "${worksrcpath}/*" ${test.dir}] } {
721                    # The test directory is inside the source directory, so put in the new source directory name.
722                    option test.dir [string map "${worksrcpath} ${worksrcpath}-${arch}" ${test.dir}]
723                } else {
724                    # The test directory is outside the source directory, so give it a new name by appending ${arch}.
725                    option test.dir ${test.dir}-${arch}
726                    if { ![file exists ${test.dir}] } {
727                        file mkdir ${test.dir}
728                    }
729                }
730
731                porttest::test_main
732
733                option test.dir ${test_dir_save}
734            }
735        }
736    }
737}
738
739# [muniversal_file_or_symlink_exists ${f}] tells you if ${f} exists. And unlike
740# [file exists ${f}], if used on a symlink, [muniversal_file_or_symlink_exists ${f}]
741# tells you about the symlink, not what it points to.
742proc muniversal_file_or_symlink_exists {f} {
743    # If [file type ${f}] throws an error, ${f} doesn't exist.
744    if {[catch {file type ${f}}]} {
745        return 0
746    }
747    # Otherwise, it does.
748    return 1
749}
Note: See TracBrowser for help on using the repository browser.