1#!/usr/bin/env bash
2set -e
3
4# This script must be able to run with bash-3.1, so it can't use
5# associative arrays. Instead, it emulates them using 'eval'. It
6# can however use indexed arrays, supported since at least bash-3.0.
7
8# The names of the br2-external trees, once validated.
9declare -a BR2_EXT_NAMES
10
11# URL to manual for help in converting old br2-external trees.
12# Escape '#' so that make does not consider it a comment.
13MANUAL_URL='https://buildroot.org/manual.html\#br2-external-converting'
14
15main() {
16    local OPT OPTARG
17    local br2_ext outputdir
18
19    while getopts :d: OPT; do
20        case "${OPT}" in
21        d)  outputdir="${OPTARG}";;
22        :)  error "option '%s' expects a mandatory argument\n" "${OPTARG}";;
23        \?) error "unknown option '%s'\n" "${OPTARG}";;
24        esac
25    done
26    # Forget options; keep only positional args
27    shift $((OPTIND-1))
28
29    if [ -z "${outputdir}" ]; then
30        error "no output directory specified (-d)\n"
31    fi
32
33    # Trap any unexpected error to generate a meaningful error message
34    trap "error 'unexpected error while generating ${ofile}\n'" ERR
35
36    mkdir -p "${outputdir}"
37    do_validate "${outputdir}" ${@//:/ }
38    do_mk "${outputdir}"
39    do_kconfig "${outputdir}"
40}
41
42# Validates the br2-external trees passed as arguments. Makes each of
43# them canonical and store them in the global arrays BR2_EXT_NAMES
44# and BR2_EXT_PATHS.
45#
46# Note: since this script is always first called from Makefile context
47# to generate the Makefile fragment before it is called to generate the
48# Kconfig snippet, we're sure that any error in do_validate will be
49# interpreted in Makefile context. Going up to generating the Kconfig
50# snippet means that there were no error.
51#
52do_validate() {
53    local outputdir="${1}"
54    local br2_ext
55    shift
56
57    if [ ${#} -eq 0 ]; then
58        # No br2-external tree is valid
59        return
60    fi
61
62    for br2_ext in "${@}"; do
63        do_validate_one "${br2_ext}"
64    done >"${outputdir}/.br2-external.mk"
65}
66
67do_validate_one() {
68    local br2_ext="${1}"
69    local br2_name br2_desc br2_ver n d
70
71    if [ ! -d "${br2_ext}" ]; then
72        error "'%s': no such file or directory\n" "${br2_ext}"
73    fi
74    if [ ! -r "${br2_ext}" -o ! -x "${br2_ext}" ]; then
75        error "'%s': permission denied\n" "${br2_ext}"
76    fi
77    if [ ! -f "${br2_ext}/external.desc" ]; then
78        error "'%s': does not have an 'external.desc'. See %s\n" \
79            "${br2_ext}" "${MANUAL_URL}"
80    fi
81    br2_name="$(sed -r -e '/^name: +(.*)$/!d; s//\1/' "${br2_ext}/external.desc")"
82    if [ -z "${br2_name}" ]; then
83        error "'%s/external.desc': does not define the name\n" "${br2_ext}"
84    fi
85    # Only ASCII chars in [A-Za-z0-9_] are permitted
86    n="$(sed -r -e 's/[A-Za-z0-9_]//g' <<<"${br2_name}" )"
87    if [ -n "${n}" ]; then
88        # Escape '$' so that it gets printed
89        error "'%s': name '%s' contains invalid chars: '%s'\n" \
90            "${br2_ext}" "${br2_name//\$/\$\$}" "${n//\$/\$\$}"
91    fi
92    eval d="\"\${BR2_EXT_PATHS_${br2_name}}\""
93    if [ -n "${d}" ]; then
94        error "'%s': name '%s' is already used in '%s'\n" \
95            "${br2_ext}" "${br2_name}" "${d}"
96    fi
97    br2_desc="$(sed -r -e '/^desc: +(.*)$/!d; s//\1/' "${br2_ext}/external.desc")"
98    if [ ! -f "${br2_ext}/external.mk" ]; then
99        error "'%s/external.mk': no such file or directory\n" "${br2_ext}"
100    fi
101    if [ ! -f "${br2_ext}/Config.in" ]; then
102        error "'%s/Config.in': no such file or directory\n" "${br2_ext}"
103    fi
104
105    # Register this br2-external tree, use an absolute canonical path
106    br2_ext="$( cd "${br2_ext}"; pwd )"
107    br2_ver="$( support/scripts/setlocalversion "${br2_ext}" )"
108    BR2_EXT_NAMES+=( "${br2_name}" )
109    eval BR2_EXT_PATHS_${br2_name}="\"\${br2_ext}\""
110    eval BR2_EXT_VERS_${br2_name}="\"\${br2_ver}\""
111    eval BR2_EXT_DESCS_${br2_name}="\"\${br2_desc:-\${br2_name}}\""
112}
113
114# Generate the .mk snippet that defines makefile variables
115# for the br2-external tree
116do_mk() {
117    local outputdir="${1}"
118    local br2_name br2_desc br2_ext br2_ver
119
120    {
121        printf '#\n# Automatically generated file; DO NOT EDIT.\n#\n'
122        printf '\n'
123
124        printf 'BR2_EXTERNAL ?='
125        for br2_name in "${BR2_EXT_NAMES[@]}"; do
126            eval br2_ext="\"\${BR2_EXT_PATHS_${br2_name}}\""
127            printf ' %s' "${br2_ext}"
128        done
129        printf '\n'
130
131        printf 'export BR2_EXTERNAL_NAMES = \n'
132        printf 'BR2_EXTERNAL_DIRS = \n'
133        printf 'BR2_EXTERNAL_MKS = \n'
134
135        if [ ${#BR2_EXT_NAMES[@]} -eq 0 ]; then
136            printf '\n'
137            printf '# No br2-external tree defined.\n'
138            return
139        fi
140
141        for br2_name in "${BR2_EXT_NAMES[@]}"; do
142            eval br2_desc="\"\${BR2_EXT_DESCS_${br2_name}}\""
143            eval br2_ext="\"\${BR2_EXT_PATHS_${br2_name}}\""
144            eval br2_ver="\"\${BR2_EXT_VERS_${br2_name}}\""
145            printf '\n'
146            printf 'BR2_EXTERNAL_NAMES += %s\n' "${br2_name}"
147            printf 'BR2_EXTERNAL_DIRS += %s\n' "${br2_ext}"
148            printf 'BR2_EXTERNAL_MKS += %s/external.mk\n' "${br2_ext}"
149            printf 'export BR2_EXTERNAL_%s_PATH = %s\n' "${br2_name}" "${br2_ext}"
150            printf 'export BR2_EXTERNAL_%s_DESC = %s\n' "${br2_name}" "${br2_desc}"
151            printf 'export BR2_EXTERNAL_%s_VERSION = %s\n' "${br2_name}" "${br2_ver}"
152        done
153    } >"${outputdir}/.br2-external.mk"
154}
155
156# Generate the kconfig snippets for the br2-external tree.
157do_kconfig() {
158    local outputdir="${1}"
159    local br2_name br2_desc br2_ext br2_ver br2
160    local -a items
161
162    items=(
163        paths
164        menus
165        toolchains
166        jpeg
167        openssl
168        skeleton
169        init
170        linux
171    )
172
173    for br2 in "${items[@]}"; do
174        {
175            printf '#\n# Automatically generated file; DO NOT EDIT.\n#\n'
176            printf '\n'
177            if [ ${#BR2_EXT_NAMES[@]} -eq 0 ]; then
178                printf '# No br2-external tree defined.\n'
179            fi
180        } >"${outputdir}/.br2-external.in.${br2}"
181    done
182    if [ ${#BR2_EXT_NAMES[@]} -eq 0 ]; then
183        return
184    fi
185
186    printf 'menu "External options"\n\n' >>"${outputdir}/.br2-external.in.menus"
187
188    {
189        printf 'config BR2_EXTERNAL_NAMES\n'
190        printf '\tstring\n'
191        # Use star-expansion: we really one a single arg.
192        printf '\tdefault "%s"\n' "${BR2_EXT_NAMES[*]}"
193        printf '\n'
194    } >>"${outputdir}/.br2-external.in.paths"
195
196    for br2_name in "${BR2_EXT_NAMES[@]}"; do
197        eval br2_desc="\"\${BR2_EXT_DESCS_${br2_name}}\""
198        eval br2_ext="\"\${BR2_EXT_PATHS_${br2_name}}\""
199        eval br2_ver="\"\${BR2_EXT_VERS_${br2_name}}\""
200
201        {
202            printf 'config BR2_EXTERNAL_%s_PATH\n' "${br2_name}"
203            printf '\tstring\n'
204            printf '\tdefault "%s"\n' "${br2_ext}"
205            printf 'config BR2_EXTERNAL_%s_VERSION\n' "${br2_name}"
206            printf '\tstring\n'
207            printf '\tdefault "%s"\n' "${br2_ver}"
208            printf '\n'
209        } >>"${outputdir}/.br2-external.in.paths"
210
211        {
212            if [ ${#BR2_EXT_NAMES[@]} -gt 1 ]; then
213                printf 'menu "%s"\n' "${br2_desc}"
214            fi
215            printf 'comment "%s (in %s)"\n' "${br2_desc}" "${br2_ext}"
216            printf 'source "%s/Config.in"\n' "${br2_ext}"
217            if [ ${#BR2_EXT_NAMES[@]} -gt 1 ]; then
218                printf 'endmenu # %s\n' "${br2_name}"
219            fi
220            printf '\n'
221        } >>"${outputdir}/.br2-external.in.menus"
222
223        if [ -f "${br2_ext}/provides/toolchains.in" ]; then
224            printf 'comment "Toolchains from: %s"\n' "${br2_desc}"
225            printf 'source "%s/provides/toolchains.in"\n' "${br2_ext}"
226            printf '\n'
227        else
228            printf '# No toolchain from: %s\n\n' "${br2_desc}"
229        fi >>"${outputdir}/.br2-external.in.toolchains"
230
231        if [ -f "${br2_ext}/provides/jpeg.in" ]; then
232            printf 'comment "jpeg from: %s"\n' "${br2_desc}"
233            printf 'source "%s/provides/jpeg.in"\n' "${br2_ext}"
234            printf '\n'
235        else
236            printf '# No jpeg from: %s\n\n' "${br2_desc}"
237        fi >>"${outputdir}/.br2-external.in.jpeg"
238
239        if [ -f "${br2_ext}/provides/openssl.in" ]; then
240            printf 'comment "openssl from: %s"\n' "${br2_desc}"
241            printf 'source "%s/provides/openssl.in"\n' "${br2_ext}"
242            printf '\n'
243        else
244            printf '# No openssl from: %s\n\n' "${br2_desc}"
245        fi >>"${outputdir}/.br2-external.in.openssl"
246
247        if [ -f "${br2_ext}/provides/skeleton.in" ]; then
248            printf 'comment "skeleton from: %s"\n' "${br2_desc}"
249            printf 'source "%s/provides/skeleton.in"\n' "${br2_ext}"
250            printf '\n'
251        else
252            printf '# No skeleton from: %s\n\n' "${br2_desc}"
253        fi >>"${outputdir}/.br2-external.in.skeleton"
254
255        if [ -f "${br2_ext}/provides/init.in" ]; then
256            printf 'comment "init from: %s"\n' "${br2_desc}"
257            printf 'source "%s/provides/init.in"\n' "${br2_ext}"
258            printf '\n'
259        else
260            printf '# No init from: %s\n\n' "${br2_desc}"
261        fi >>"${outputdir}/.br2-external.in.init"
262
263        if [ -f "${br2_ext}/linux/Config.ext.in" ]; then
264            printf 'comment "linux extension from: %s"\n' "${br2_desc}"
265            printf 'source "%s/linux/Config.ext.in"\n' "${br2_ext}"
266            printf '\n'
267        else
268            printf '# No linux extension from: %s\n\n' "${br2_desc}"
269        fi >>"${outputdir}/.br2-external.in.linux"
270    done
271
272    printf 'endmenu\n' >>"${outputdir}/.br2-external.in.menus"
273}
274
275error() { local fmt="${1}"; shift; printf "BR2_EXTERNAL_ERROR = ${fmt}" "${@}"; exit 1; }
276
277my_name="${0##*/}"
278main "${@}"
279