1#!/bin/sh
2
3#
4#    Copyright 2014-2018 Nest Labs Inc. All Rights Reserved.
5#    Copyright 2018 Google LLC. All Rights Reserved.
6#
7#    Licensed under the Apache License, Version 2.0 (the "License");
8#    you may not use this file except in compliance with the License.
9#    You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#    Unless required by applicable law or agreed to in writing, software
14#    distributed under the License is distributed on an "AS IS" BASIS,
15#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16#    See the License for the specific language governing permissions and
17#    limitations under the License.
18#
19
20#
21#    Description:
22#      This file is a convenience script for building, for the current
23#      build host system, the minimal, core set of GNU autotools on
24#      which other projects' build systems depend.
25#
26
27EXTENSIONS="tar.gz tar.xz"
28
29VERBOSE=0
30
31stem=tools/packages
32abs_srcdir=`pwd`
33abs_top_srcdir=`echo ${abs_srcdir} | sed -e s,/${stem},,g`
34
35abs_top_hostdir="${abs_top_srcdir}/tools/host"
36
37#
38# usage
39#
40# Display program usage.
41#
42usage() {
43    name=`basename ${0}`
44
45    echo "Usage: ${name} [ options ] [ -- <package> ... ]"
46
47    if [ $1 -ne 0 ]; then
48        echo "Try '${name} -h' for more information."
49    fi
50
51    if [ $1 -ne 1 ]; then
52        echo ""
53        echo "  --arch ARCH      Build the package for ARCH host architecture."
54        echo "  -h, --help       Print this help, then exit."
55        echo "  --builddir DIR   Build the packages in DIR."
56        echo "  --destdir DIR    Install the built tools to directory DIR."
57        echo "  --srcdir DIR     Find package, version, and URL metadata in DIR."
58        echo "  -v, --verbose    Enable verbose output."
59        echo ""
60    fi
61
62    exit $1
63}
64
65#
66# error <...>
67#
68# Display to standard error the specified arguments
69#
70error() {
71    echo "${*}" >&2
72}
73
74#
75# verbose <...>
76#
77# Display to standard error the specified arguments
78#
79verbose() {
80    echo "${*}" >&2
81}
82
83#
84# separator
85#
86# Display to standard error a log separator.
87#
88separator() {
89    verbose "--------------------------------------------------------------------------------"
90}
91
92#
93# trailer
94#
95# Display to standard error a log trailer.
96#
97trailer() {
98    verbose "================================================================================"
99}
100
101#
102# banner <message ...>
103#
104# Display to standard error a log banner with a message.
105#
106banner() {
107    trailer
108
109    verbose "${*}"
110}
111
112#
113# subbanner <message ...>
114#
115# Display to standard error a log subbanner with a message.
116#
117subbanner() {
118    separator
119
120    verbose "${*}"
121}
122
123#
124# find_archive <subdir> <package> <extensions...>
125#
126# Attempt to find the specified package archive in the provided
127# subdirectory matching one of the specified extensions.
128#
129find_archive() {
130    local subdir="${1}"
131    local package="${2}"
132    local archive
133
134    shift 2
135
136    for extension in ${*}; do
137        local maybe_archive="${subdir}/${package}.${extension}"
138
139        verbose "  FIND     ${maybe_archive}"
140
141        if [ -r "${maybe_archive}" ]; then
142            archive="${maybe_archive}"
143            break
144        fi
145    done
146
147    echo "${archive}"
148}
149
150#
151# find_versioned_or_nonversioned_archive <subdir> <package> <version> <extensions...>
152#
153# Attempt to find the specified version-qualified or non-qualified
154# package archive in the provided subdirectory matching one of the
155# specified extensions.
156#
157find_versioned_or_nonverioned_archive() {
158    local subdir="${1}"
159    local package="${2}"
160    local version="${3}"
161    local fqpackage="${package}-${version}"
162    local archive
163
164    shift 3
165
166    archive="`find_archive ${subdir} ${fqpackage} ${*}`"
167
168    if [ -z "${archive}" ]; then
169        archive="`find_archive ${subdir} ${package} ${*}`"
170    fi
171
172    echo "${archive}"
173}
174
175#
176# fetch_url_with_command <fetchdir> <url> <command ...>
177#
178# Attempt to fetch the specified URL to the provided directory with
179# the provided command.
180#
181fetch_url_with_command() {
182    local fetchdir="${1}"
183    local url="${2}"
184    local executable="`which ${3}`"
185    local curdir="`pwd`"
186    local status=1
187
188    shift 2
189
190    if [ -x "${executable}" ]; then
191        cd "${fetchdir}"
192
193            verbose "  `echo ${1} | tr '[[:lower:]]' '[[:upper:]]'`     ${url}"
194
195            ${*} "${url}"
196
197            status=${?}
198
199            if [ ${?} -ne 0 ]; then
200                    error "Failed to fetch ${url} with ${1}."
201            fi
202
203        cd "${curdir}"
204    fi
205
206    return ${status}
207}
208
209#
210# fetch_url <fetchdir> <url>
211#
212# Attempt to fetch the specified URL to the provided directory with
213# either wget or curl.
214#
215fetch_url() {
216    local fetchdir="${1}"
217    local url="${2}"
218
219    # Try to fetch the package using wget or curl
220
221    fetch_url_with_command "${fetchdir}" "${url}" wget --tries 4 --no-check-certificate --quiet ||
222        fetch_url_with_command "${fetchdir}" "${url}" curl --retry 4 --insecure --silent --remote-name
223}
224
225#
226# fetch_package <finddir> <fetchdir> <package> <version> <url>
227#
228# Attempt to fetch the specified package version to <fetchdir>, if a
229# local archive does not already exist in <finddir> or <fetchdir>,
230# from the provided URL.
231#
232fetch_package() {
233    local finddir="${1}"
234    local fetchdir="${2}"
235    local package="${3}"
236    local version="${4}"
237    local url="${5}"
238    local fqpackage="${package}-${version}"
239
240    # Check whether a local archive already exists in <finddir> or <fetchdir>.
241
242    archive="`find_versioned_or_nonverioned_archive ${finddir} ${package} ${version} ${EXTENSIONS}`"
243
244    if [ -z "${archive}" ]; then
245        archive="`find_versioned_or_nonverioned_archive ${fetchdir} ${package} ${version} ${EXTENSIONS}`"
246    fi
247
248    # If no archive was found, attempt to fetch it.
249
250    if [ -z "${archive}" ]; then
251        verbose "  FETCH    ${url}"
252
253        fetch_url "${fetchdir}" "${url}"
254
255    else
256        error "No, found ${archive} locally."
257
258    fi
259}
260
261#
262# removetmp
263#
264# Remove temporary files and directories used during the run of this
265# script.
266#
267removetmp() {
268    if [ -O "${LIBTOOLIZE}" ]; then
269        rm -f "${LIBTOOLIZE}"
270    fi
271    if [ -n "${AUTOM4TE_CFG}" ]; then
272        rm -f "${AUTOM4TE_CFG}"
273    fi
274}
275
276#
277# patch_directory <directory> <patch arguments> <patch file> [ ... ]
278#
279# Patch the specified directory by applying the provided patched files
280# using the specified patch arguments. The specified patch files may
281# be uncompressed or compressed with any of bz2, gz, xz, compress, or
282# zip.
283#
284patch_directory() {
285    local directory="${1}"
286    local patchargs="${2}"
287
288    shift 2
289
290    verbose "  PATCH    ${directory}"
291
292    for patch in "${*}"; do
293        verbose "  PATCH    ${patch}"
294
295        extension=`echo "${patch}" | awk -F . '{if (NF > 1) {print $$NF}}'`;
296
297        case "${extension}" in
298
299            bz2)
300                uncompressor="bunzip2 -c"
301                ;;
302
303            gz)
304                uncompressor="gunzip -c"
305                ;;
306
307            xz)
308                uncompressor="xz -d -c"
309                ;;
310
311            Z)
312                uncompressor="uncompress -c"
313                ;;
314
315            zip)
316                uncompressor="unzip -p"
317                ;;
318
319            *)
320                uncompressor="cat"
321                ;;
322
323        esac
324
325        ${uncompressor} "${patch}" | patch ${patchargs} -d "${directory}" || exit 1;
326
327    done
328}
329
330#
331# build_package <package> <version> <host> <patchdir> <archivedir> <builddir> <destdir>
332#
333# Build the specified package version for <host> in <builddir> from
334# the archive found in <archivedir>, after applying the patches in
335# <patchdir> (if any), and install it into the specified destination
336# directory, <destdir>.
337#
338build_package() {
339    local package="${1}"
340    local version="${2}"
341    local host="${3}"
342    local patchdir="${4}"
343    local archivedir="${5}"
344    local builddir="${6}"
345    local destdir="${7}"
346    local fqpackage="${package}-${version}"
347    local curdir=`pwd`
348    local archive
349
350    verbose "  CHECK    ${package}"
351
352    archive="`find_versioned_or_nonverioned_archive ${archivedir} ${package} ${version} ${EXTENSIONS}`"
353
354    if [ -z "${archive}" ]; then
355        error "Could not find an archive for ${package}."
356        exit 1
357    fi
358
359    if [ ! -d "${archivedir}/${fqpackage}" ]; then
360        verbose "  TAR      ${archive}"
361        tar --directory "${archivedir}" -xf "${archive}" || exit ${?}
362    fi
363
364    # If necessary, patch the expanded package.
365
366    if [ -d "${patchdir}" ]; then
367	patch_directory "${archivedir}/${fqpackage}" "-s -p1" ${patchdir}/*.patch*
368    fi
369
370    # If possible, attempt to be self-sufficient, relying on GNU autotools
371    # executables installed along with the SDK itself.
372
373    if [ -d "${destdir}/${host}/bin" ]; then
374        export PATH="${destdir}/${host}/bin:${PATH}"
375    fi
376
377    if [ -d "${destdir}/bin" ]; then
378        export PATH="${destdir}/bin:${PATH}"
379    fi
380
381    export ACLOCAL=`which aclocal`
382    export AUTOCONF="`which autoconf`"
383    export AUTOHEADER="`which autoheader`"
384    export AUTOM4TE="`which autom4te`"
385    export AUTOMAKE="`which automake`"
386    export LIBTOOLIZE="`which libtoolize || which glibtoolize`"
387    export M4=`which m4`
388
389    # Establish, if necessary, some SDK-specific directories needed to
390    # override various paths in GNU autotools that otherwise expect to be
391    # absolute (e.g. /usr/share, etc.).
392
393    if [ -d "${destdir}/share" ]; then
394        if [ -d "${destdir}/share/autoconf" ]; then
395            export AC_MACRODIR="${destdir}/share/autoconf"
396
397            export autom4te_perllibdir="${destdir}/share/autoconf"
398        fi
399
400        if [ -d "${destdir}/share/automake-1.14" ]; then
401            export PERL5LIB="${destdir}/share/automake-1.14:${PERL5LIB}"
402        fi
403    fi
404
405    trap "removetmp" 1 2 3 9 15
406
407    #
408    # Generate any temporary files that need to be patched at run time
409    # with the location of the SDK tree, including:
410    #
411    #   -  The autom4te configuration file
412    #   -  The libtoolize executable script
413    #
414
415    if [ -r "${destdir}/share/autoconf/autom4te.cfg" ]; then
416        export AUTOM4TE_CFG="${destdir}/autom4te.cfg"
417
418        sed -e "s,//share/autoconf,${destdir}/share/autoconf,g" < "${destdir}/share/autoconf/autom4te.cfg" > "${AUTOM4TE_CFG}"
419    fi
420
421    if [ -r "${destdir}/${host}/bin/libtoolize" ]; then
422        export LIBTOOLIZE="${destdir}/libtoolize"
423
424        sed -e "s,//share/libtool,${destdir}/share/libtool,g" -e "s,//share/aclocal,${destdir}/share/aclocal,g" < "${destdir}/${host}/bin/libtoolize" > "${LIBTOOLIZE}"
425
426        chmod 775 "${LIBTOOLIZE}"
427    fi
428
429    # Configure the package
430
431    if [ ${VERBOSE} -gt 0 ]; then
432        CONFIGURE_FLAGS=""
433    else
434        CONFIGURE_FLAGS="--quiet"
435    fi
436
437    verbose "  CONFIG   ${package}"
438
439    cd "${builddir}"
440
441        ${archivedir}/${fqpackage}/configure ${CONFIGURE_FLAGS} --prefix=/ --exec-prefix=/${host} || exit ${?}
442
443    cd "${curdir}"
444
445    # Build the package
446
447    verbose "  MAKE     ${package}"
448
449    make V=${VERBOSE} -C "${builddir}" all || exit ${?}
450
451    # Install / stage the package
452
453    verbose "  INSTALL  ${package}"
454
455    make V=${VERBOSE} -C "${builddir}" DESTDIR="${destdir}" install || exit ${?}
456
457    # Remove any temporary files created.
458
459    removetmp
460}
461
462# Parse out any command line options
463
464while [ ${#} -gt 0 ]; do
465    case ${1} in
466        --arch)
467            ARCH=${2}
468            shift 2
469            ;;
470
471        -h|--help)
472            usage 0
473            ;;
474
475        --builddir)
476            BUILDDIR=${2}
477            shift 2
478            ;;
479
480        --destdir)
481            DESTDIR=${2}
482            shift 2
483            ;;
484
485        --srcdir)
486            SRCDIR=${2}
487            shift 2
488            ;;
489
490        -v|--verbose)
491            $((VERBOSE++))
492            shift 1
493            ;;
494
495        --)
496            shift 1
497            break
498            ;;
499
500        *)
501            usage 1
502            ;;
503    esac
504done
505
506# If the architecture is not specified, fail.
507
508if [ -z "${ARCH}" ]; then
509    error "The host architecture was not specified via --arch."
510
511    usage 1
512fi
513
514# If the --builddir option wasn't specified, then provide a default.
515
516if [ -z "${BUILDDIR}" ]; then
517    BUILDDIR="`pwd`"
518fi
519
520# If the --destdir option wasn't specified, then provide a default.
521
522if [ -z "${DESTDIR}" ]; then
523    DESTDIR=${abs_top_hostdir}
524fi
525
526# If the --srcdir option wasn't specified, then provide a default.
527
528if [ -z "${SRCDIR}" ]; then
529    SRCDIR="`pwd`"
530fi
531
532# Determine what packages to build
533
534if [ ${#} -gt 0 ]; then
535    PACKAGES="${*}"
536
537else
538    PACKAGES="`cat ${SRCDIR}/packages`"
539
540fi
541
542# Build each package
543
544banner "Building GNU autotools for ${ARCH}..."
545
546for package in ${PACKAGES}; do
547    url="`cat ${SRCDIR}/${package}/${package}.url`"
548    version="`cat ${SRCDIR}/${package}/${package}.version`"
549    patchdir="${SRCDIR}/${package}/${package}.patches"
550
551    # Fetch, if necessary, the package from the canonical source location.
552
553    subbanner "  CHECK    ${package}"
554
555    fetch_package "${SRCDIR}/${package}" "${BUILDDIR}" "${package}" "${version}" "${url}"
556
557    # Build and install the package.
558
559    verbose "  MKDIR    ${BUILDDIR}/build/${ARCH}/${package}-${version}"
560
561    mkdir -p "${BUILDDIR}/build/${ARCH}/${package}-${version}" || exit ${?}
562
563    build_package "${package}" "${version}" "${ARCH}" "${patchdir}" "${BUILDDIR}" "${BUILDDIR}/build/${ARCH}/${package}-${version}" "${DESTDIR}"
564done
565
566trailer
567