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

Last change on this file was 146912, checked in by mcalhoun@…, 3 years ago

muniversal-1.0.tcl: ignore timestamp difference in Python byte code files

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