1#!/bin/sh
2#
3# Copyright (C) 2010 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18. `dirname $0`/prebuilt-common.sh
19PROGDIR=`dirname $0`
20PROGNAME=`basename $0`
21
22if [ -z "$ANDROID_PRODUCT_OUT" ] ; then
23    echo "ERROR: The environment variable ANDROID_PRODUCT_OUT is not defined!"
24fi
25
26# This sets HOST_TAG to linux-x86 or darwin-x86 on 64-bit systems
27force_32bit_binaries
28
29# We need to extract the auto-detected platform level to display it in
30# the help. Do not barf if we can't do it right now, to keep --help working
31# in this case.
32#
33BUILD_PROP=$ANDROID_PRODUCT_OUT/system/build.prop
34if [ ! -f "$BUILD_PROP" ] ; then
35    # Use this as default if the build.prop file does not exist
36    # We will generate an error after the extract_parameters call
37    # in this specific case, but we want --help to work before that.
38    PLATFORM=9
39else
40    PLATFORM=`awk -F '=' '$1 == "ro.build.version.sdk" { print $2; }' $BUILD_PROP`
41    if [ $? != 0 ] ; then
42        dump "WARNING: Could not extract default platform level from $BUILD_PROP!"
43        PLATFORM=9
44        dump "Defaulting to $PLATFORM"
45    fi
46fi
47
48PROGRAM_PARAMETERS="<level> [<level2>...]"
49PROGRAM_DESCRIPTION=\
50"This script is used to update the NDK's platform headers and system libraries
51from those generated after a full build of the Android platform. Run it after
52modifying and rebuilding one of the public libraries exposed through the NDK.
53
54The first parameter must be a platform/API level. For example, to update the
55NDK definitions for the 'android-9' platform, use one of:
56
57    $PROGNAME android-9
58    $PROGNAME 9
59
60You can also use several values to update several levels at once, e.g.:
61
62    $PROGNAME $API_LEVELS
63
64NOTE: The currently auto-detected API level for your build is $PLATFORM,
65      but this value may be incorrect if your platform has not been assigned
66      a new API level yet.
67
68This script is really in charge of the following tasks:
69
70  1/ Import system headers from \$ANDROID/framework/base/ and other
71     locations in the full system source tree.
72
73  2/ Locate system shared libraries from \$ANDROID_PRODUCT_OUT/system/lib
74     and convert them into small \"shell\" .so files that only export the
75     same functions and variables. These can be used with the NDK at link
76     time, are much smaller, and also do not require the use of special
77     linker flags when used with the standalone NDK toolchain
78     (i.e. -Wl,--allow-shlib-undefined)
79
80  3/ For each shared library, also generate a list of symbols exported
81     by the shell. This makes it easier to see with 'git diff' which
82     symbols were added (or even removed) since the last invocation.
83
84  4/ Copy a few system static libraries (libc.a, libm.a, etc...) used
85     to generate static executables. As well as a few key object files
86     required by the C runtime (e.g. crtbegin_dynamic.o), when needed.
87
88By default, all files are placed under \$ANDROID/development/ndk
89but you can override this with the --out-dir=<path> option.
90
91By default, the build-specific platform/API level is autodetected, and
92only the files under \$ANDROID_ROOT/development/ndk/platforms/android-<level>/
93will be affected. This ensures you don't accidentally overwrite files
94corresponding to previous releases.
95"
96
97ARCH=arm
98register_var_option "--arch=<name>" ARCH "Specify architecture name."
99
100FORCE=no
101register_var_option "--force" FORCE "Force-copy all files."
102
103DEVDIR="$ANDROID_NDK_ROOT/../development/ndk"
104if [ -d "$DEVDIR" ] ; then
105    OUT_DIR=`cd $DEVDIR && pwd`
106else
107    OUT_DIR=
108fi
109register_var_option "--out-dir=<path>" OUT_DIR "Specify output directory."
110
111TOOLCHAIN_PREFIX=
112register_var_option "--toolchain-prefix=<path>" TOOLCHAIN_PREFIX "Path and prefix for the toolchain"
113log "Toolchain prefix: $TOOLCHAIN_PREFIX"
114
115extract_parameters "$@"
116
117if [ -z "$PARAMETERS" ] ; then
118    dump "ERROR: Missing required API/platform level. See --help for usage instructions."
119    exit 1
120fi
121
122# Normalize platform names, i.e. get rid of android- prefix in a list of
123# platform levels
124#
125#  3 android-4 foo-5 -> 3 4 foo-5  (foo-5 is invalid name)
126#
127# $1: list of platform levels
128#
129normalize_platforms ()
130{
131    local RET=$(echo "$@" | tr ' ' '\n' | sed -e 's!android-!!g' | tr '\n' ' ')
132    echo ${RET%% }
133}
134
135PLATFORMS=`normalize_platforms $PARAMETERS`
136log "Target platform levels: $PLATFORMS"
137
138if [ "$FORCE" = "yes" ] ;then
139    # We can't accept several platform levels in --force mode.
140    NUM_PLATFORMS=$(echo $PLATFORMS | tr ' ' '\n' | wc -l)
141    if [ "$NUM_PLATFORMS" != 1 ]; then
142        echo "ERROR: You can only use a single platform level when using --force ($NUM_PLATFORMS)"
143        exit 1
144    fi
145fi
146
147# Return the list of files under a given directory
148# $1: directory
149# $2: (optional) file patterns
150list_regular_files_in ()
151{
152    local DIR="$1"
153    shift
154    local PATTERNS="$@"
155    if [ -z "$PATTERNS" ]; then
156        PATTERNS="."
157    fi
158    cd "$DIR" && find $PATTERNS -type f | sed -e 's!^./!!g'
159}
160
161# Check that a given platform level was listed on the command line
162# $1: Platform numerical level (e.g. '3')
163# returns true if the platform is listed
164platform_check ()
165{
166    if [ "$FORCE" = "yes" ]; then
167        PLATFORM_ROOT="$OUT_DIR/platforms/android-$PLATFORMS/arch-$ARCH"
168        log "Platform root (forced): $PLATFORM_ROOT"
169        return 0
170    fi
171    echo "$PLATFORMS" | tr ' ' '\n' | fgrep -q "$1"
172    if [ $? != 0 ] ; then
173        # Not listed, return an error code for 'if'
174        return 1
175    else
176        PLATFORM_ROOT="$OUT_DIR/platforms/android-$1/arch-$ARCH"
177        log "Platform root: $PLATFORM_ROOT"
178        return 0
179    fi
180}
181
182# Determine Android build tree root
183ANDROID_ROOT=`cd $ANDROID_PRODUCT_OUT/../../../.. && pwd`
184log "Android build tree root: $ANDROID_ROOT"
185log "Android product out: $ANDROID_PRODUCT_OUT"
186
187case $ARCH in
188    arm)
189        TOOLCHAIN=$DEFAULT_ARCH_TOOLCHAIN_NAME_arm
190        PREFIX=$DEFAULT_ARCH_TOOLCHAIN_PREFIX_arm
191        ;;
192    x86)
193        TOOLCHAIN=$DEFAULT_ARCH_TOOLCHAIN_NAME_x86
194        PREFIX=$DEFAULT_ARCH_TOOLCHAIN_PREFIX_x86
195        ;;
196    mips)
197        TOOLCHAIN=$DEFAULT_ARCH_TOOLCHAIN_NAME_mips
198        PREFIX=$DEFAULT_ARCH_TOOLCHAIN_PREFIX_mips
199        ;;
200    *)
201        echo "ERROR: Unsupported architecture: $ARCH"
202        exit 1
203esac
204
205if [ -z "$TOOLCHAIN_PREFIX" ]; then
206    TOOLCHAIN_NAME=$(get_default_toolchain_name_for_arch $ARCH)
207    TOOLCHAIN_PREFIX=$(get_default_toolchain_prefix_for_arch $ARCH)
208    TOOLCHAIN_PREFIX=$(get_toolchain_install $ANDROID_NDK_ROOT $TOOLCHAIN_NAME)/bin/$TOOLCHAIN_PREFIX
209    TOOLCHAIN_PREFIX=${TOOLCHAIN_PREFIX%%-}
210    if [ -z "$TOOLCHAIN_PREFIX" ]; then
211        echo "ERROR: Unsupported architecture"
212        exit 1
213    fi
214    echo "Autoconfig: --toolchain-prefix=$TOOLCHAIN_PREFIX"
215fi
216
217if [ ! -d "$(dirname $TOOLCHAIN_PREFIX)" ]; then
218    echo "ERROR: Toolchain not installed, missing directory: $(dirname $TOOLCHAIN_PREFIX)"
219    exit 1
220fi
221
222if [ ! -f "$TOOLCHAIN_PREFIX-readelf" ]; then
223    echo "ERROR: Toolchain not installed, missing program: $TOOLCHAIN_PREFIX-readelf"
224    exit 1
225fi
226
227if [ -z "$OUT_DIR" ] ; then
228    dump "ERROR: Could not find valid output directory (e.g. \$NDK/../development/ndk)."
229    dump "Please use --out-dir=<path> to specify one!"
230    exit 1
231fi
232
233# Check the platform value and set PLATFORM_ROOT
234#
235
236# Normalize the value: android-3 -> android-3   3 -> android-3
237PLATFORM=${PLATFORM##android-}
238PLATFORM=android-$PLATFORM
239
240
241# Temp file used to list shared library symbol exclusions
242# See set_symbol_excludes and extract_shared_library_xxxx functions below
243SYMBOL_EXCLUDES=$TMPDIR/ndk-symbol-excludes.txt
244
245# Temp file used to list shared library symbol inclusions, these
246# are essentially overrides to the content of SYMBOL_EXCLUDES
247SYMBOL_INCLUDES=$TMPDIR/ndk-symbol-includes.txt
248
249# Reset the symbol exclusion list to its default
250reset_symbol_excludes ()
251{
252    # By default, do not export C++ mangled symbol, which all start with _Z
253    echo '^_Z' > $SYMBOL_EXCLUDES
254    > $SYMBOL_INCLUDES
255}
256
257# Add new exclusion patterns to SYMBOL_EXCLUDES
258set_symbol_excludes ()
259{
260    (echo "$@" | tr ' ' '\n') >> $SYMBOL_EXCLUDES
261}
262
263# Add new inclusion patterns to SYMBOL_INCLUDES
264set_symbol_includes ()
265{
266    (echo "$@" | tr ' ' '\n') >> $SYMBOL_INCLUDES
267}
268
269# Clear symbol exclusion/inclusion files
270clear_symbol_excludes ()
271{
272    rm -f $SYMBOL_EXCLUDES $SYMBOL_INCLUDES
273}
274
275# Filter the list of symbols from a file
276# $1: path to symbol list file
277filter_symbols ()
278{
279    (grep -v -f $SYMBOL_EXCLUDES $1 ; grep -f $SYMBOL_INCLUDES $1) | sort -u
280}
281
282#
283# Dump the list of dynamic functions exported by a given shared library
284# $1: Path to shared library object
285extract_shared_library_functions ()
286{
287    $TOOLCHAIN_PREFIX-readelf -s -D -W $1 | awk '$5 ~ /FUNC/ && $6 ~ /GLOBAL/ && $8 !~ /UND/ { print $9; }' > $TMPC
288    filter_symbols $TMPC
289}
290
291# Dump the list of global variables exposed by a given shared library
292# $1: Path to shared library object
293extract_shared_library_variables ()
294{
295    $TOOLCHAIN_PREFIX-readelf -s -D -W $1 | awk '$5 ~ /OBJECT/ && $6 ~ /GLOBAL/ && $8 !~ /UND/ { print $9; }' > $TMPC
296    filter_symbols $TMPC
297}
298
299# Generate link library, i.e. a special tiny shell .so that exports the
300# same symbols as a reference shared library, and can be used during
301# link with the NDK toolchain.
302#
303# Having these shells allows two things:
304#
305# - Reduce the size of the NDK release package (some libs are very large)
306# - Use the standalone toolchain without -Wl,--allow-shlib-undefined
307#
308# Note that the list of symbols for each generated library is stored
309# under arch-$ARCH/symbols/<libname>.txt
310#
311# $1: Path to reference shared library
312# $2: Path to output shared library (can be the same as $1)
313#
314generate_shell_library ()
315{
316    # First, extract the list of functions and variables exported by the
317    # reference library.
318    local funcs="`extract_shared_library_functions $1`"
319    local vars="`extract_shared_library_variables $1`"
320    local numfuncs=`echo $funcs | wc -w`
321    local numvars=`echo $vars | wc -w`
322    dump "Generating shell library for `basename $1` ($numfuncs functions + $numvars variables)"
323
324    # Now generate a small C source file that contains similarly-named stubs
325    echo "/* Auto-generated file, do not edit */" > $TMPC
326    local func var
327    for func in $funcs; do
328        echo "void $func(void) {}" >> $TMPC
329    done
330    for var in $vars; do
331        echo "int $var;" >> $TMPC
332    done
333
334    # Build it with our cross-compiler. It will complain about conflicting
335    # types for built-in functions, so just shut it up.
336    $TOOLCHAIN_PREFIX-gcc -Wl,-shared,-Bsymbolic -nostdlib -o $TMPO $TMPC 1>/dev/null 2>&1
337    if [ $? != 0 ] ; then
338        dump "ERROR: Can't generate shell library for: $1"
339        dump "See the content of $TMPC for details."
340        exit 1
341    fi
342
343    # Sanity check: the generated shared library must export the same
344    # functions and variables, or something is really rotten!
345    local newfuncs="`extract_shared_library_functions $TMPO`"
346    local newvars="`extract_shared_library_variables $TMPO`"
347    if [ "$newfuncs" != "$funcs" ] ; then
348        dump "ERROR: mismatch in generated functions list"
349        exit 1
350    fi
351    if [ "$newvars" != "$vars" ] ; then
352        dump "ERROR: mismatch in generated variables list"
353        exit 1
354    fi
355
356    # Copy to our destination now
357    local libdir=`dirname "$2"`
358    mkdir -p "$libdir" && cp -f $TMPO "$2"
359    if [ $? != 0 ] ; then
360        dump "ERROR: Can't copy shell library for: $1"
361        dump "target location is: $2"
362        exit 1
363    fi
364
365    # Write the functions and variables to a symbols file now
366    local symdir=`dirname "$libdir"`/symbols
367    local symfile="$symdir/`basename $1.txt`"
368
369    dump "Generating symbol file: $symfile"
370    mkdir -p "$symdir" && > $symfile
371    (echo "$funcs" | tr ' ' '\n') >>     $symfile
372    (echo "$vars" | tr ' ' '\n') >> $symfile
373
374    # Clear the export exclusion list
375    reset_symbol_excludes
376}
377
378# 'Copy' a given system library. This really reads the shared library to
379# to generate a small shell version that will be installed at the destination
380# $1: Library name (e.g. libEGL.so)
381#
382copy_system_shared_library ()
383{
384    local src="$ANDROID_PRODUCT_OUT/system/lib/$1.so"
385    if [ ! -f "$src" ] ; then
386        dump "ERROR: Missing system library: $src"
387        exit 1
388    fi
389    local dst="$PLATFORM_ROOT/lib/$1.so"
390    mkdir -p `dirname "$dst"` &&
391    generate_shell_library "$src" "$dst"
392}
393
394copy_system_static_library ()
395{
396    local src="$ANDROID_PRODUCT_OUT/obj/STATIC_LIBRARIES/$1_intermediates/$1.a"
397    if [ ! -f "$src" ] ; then
398        dump "ERROR: Missing system static library: $src"
399        exit 1
400    fi
401    local dst="$PLATFORM_ROOT/lib/$1.a"
402    dump "Copying system static library: $1.a"
403    mkdir -p `dirname "$dst"` && cp -f "$src" "$dst"
404}
405
406copy_system_object_file ()
407{
408    local src="$ANDROID_PRODUCT_OUT/obj/lib/$1.o"
409    if [ ! -f "$src" ] ; then
410        dump "ERROR: Missing system object file: $src"
411        exit 1
412    fi
413    local dst="$PLATFORM_ROOT/lib/$1.o"
414    dump "Copying system object file: $1.o"
415    mkdir -p `dirname "$dst"` &&
416    cp -f "$src" "$dst"
417}
418
419# Copy the content of a given directory to $SYSROOT/usr/include
420# $1: Source directory
421# $2+: List of headers
422copy_system_headers ()
423{
424    local srcdir="$1"
425    shift
426    local header
427    dump "Copying system headers from: $srcdir"
428    for header; do
429        dump "  $header"
430        local src="$srcdir/$header"
431        local dst="$PLATFORM_ROOT/../include/$header"
432        if [ ! -f "$srcdir/$header" ] ; then
433            dump "ERROR: Missing system header: $srcdir/$header"
434            exit 1
435        fi
436        mkdir -p `dirname "$dst"` && cp -f "$src" "$dst"
437        if [ $? != 0 ] ; then
438            dump "ERROR: Could not copy system header: $src"
439            dump "Target location: $dst"
440            exit 1
441        fi
442    done
443}
444
445# Copy all headers found under $1
446# $1: source directory
447copy_system_headers_from ()
448{
449    local headers=$(list_regular_files_in "$1")
450    copy_system_headers $1 $headers
451}
452
453# Same as copy_system_headers, but for arch-specific files
454# $1: Source directory
455# $2+: List of headers
456copy_arch_system_headers ()
457{
458    local srcdir="$1"
459    shift
460    local header
461    for header; do
462        dump "Copying $arch system header: $header from $srcdir"
463        local src="$srcdir/$header"
464        local dst="$PLATFORM_ROOT/include/$header"
465        if [ ! -f "$srcdir/$header" ] ; then
466            dump "ERROR: Missing $ARCH system header: $srcdir/$header"
467            exit 1
468        fi
469        mkdir -p `dirname "$dst"` && cp -f "$src" "$dst"
470        if [ $? != 0 ] ; then
471            dump "ERROR: Could not copy $ARCH system header: $src"
472            dump "Target location: $dst"
473            exit 1
474        fi
475    done
476}
477
478copy_arch_system_headers_from ()
479{
480    local headers=$(list_regular_files_in "$1")
481    copy_arch_system_headers $1 $headers
482}
483
484copy_arch_kernel_headers_from ()
485{
486    local headers=$(list_regular_files_in "$1" asm)
487    copy_arch_system_headers $1 $headers
488}
489
490# Now do the work
491
492reset_symbol_excludes
493
494# API level 3
495if platform_check 3; then
496    # Remove a few internal symbols that should not be exposed
497    # from the C library (we plan to clean that up soon by using the
498    # "hidden" visibility attribute in the near future).
499    #
500    set_symbol_excludes \
501        '^the_' '^dns_' 'load_domain_search_list' 'res_get_dns_changed' \
502        '^_resolv_cache' '^_dns_getht' '^_thread_atexit' \
503        '^free_malloc_leak_info' 'fake_gmtime_r' 'fake_localtime_r' \
504        '^gAllocationsMutex' '^gHashTable' '^gMallocLeakZygoteChild' \
505
506    copy_system_shared_library libc
507    copy_system_static_library libc
508    copy_system_headers_from $ANDROID_ROOT/bionic/libc/include
509    copy_arch_system_headers_from $ANDROID_ROOT/bionic/libc/arch-$ARCH/include
510    copy_arch_kernel_headers_from $ANDROID_ROOT/bionic/libc/kernel/arch-$ARCH
511
512    copy_system_object_file crtbegin_dynamic
513    copy_system_object_file crtbegin_static
514    copy_system_object_file crtend_android
515    case $ARCH in
516    x86)
517        copy_system_object_file crtbegin_so
518        copy_system_object_file crtend_so
519        ;;
520    esac
521
522    copy_system_shared_library libm
523    copy_system_static_library libm
524    copy_system_headers $ANDROID_ROOT/bionic/libm/include math.h
525    case "$ARCH" in
526    x86 )
527        copy_arch_system_headers $ANDROID_ROOT/bionic/libm/include/i387 fenv.h
528        ;;
529    * )
530        copy_arch_system_headers $ANDROID_ROOT/bionic/libm/$ARCH fenv.h
531        ;;
532    esac
533
534    # The <dlfcn.h> header was already copied from bionic/libc/include
535    copy_system_shared_library libdl
536    # There is no libdl.a at the moment, we might need one in
537    # the future to build gdb-7.1.x though.
538
539    copy_system_shared_library libz
540    copy_system_static_library libz
541    copy_system_headers $ANDROID_ROOT/external/zlib zconf.h zlib.h
542
543    set_symbol_excludes '^.*'         # exclude everything
544    set_symbol_includes '^__android_' # except __android_xxxx functions
545    copy_system_shared_library liblog
546    copy_system_headers $ANDROID_ROOT/system/core/include android/log.h
547
548    # NOTE: We do not copy the C++ headers, they are part of the NDK
549    #        under $NDK/source/cxx-stl. They were separated from the rest
550    #        of the platform headers in order to make room for other STL
551    #        implementations (e.g. STLport or GNU Libstdc++-v3)
552    #
553    # This is the only library that is allowed to export C++ symbols for now.
554    set_symbol_includes '^_Z.*'
555    copy_system_shared_library libstdc++
556    copy_system_static_library libstdc++
557
558    # We link gdbserver statically with libthreadb, so there is no point
559    # in copying the shared library (which by the way has an unstable ABI
560    # anyway).
561    copy_system_static_library libthread_db
562    copy_system_headers $ANDROID_ROOT/bionic/libthread_db/include thread_db.h sys/procfs.h
563
564    copy_system_headers $ANDROID_ROOT/libnativehelper/include/nativehelper jni.h
565fi
566
567# API level 4
568if platform_check 4; then
569    copy_system_shared_library libGLESv1_CM
570    copy_system_headers $ANDROID_ROOT/frameworks/base/opengl/include \
571        GLES/gl.h \
572        GLES/glext.h \
573        GLES/glplatform.h
574
575    copy_system_headers $ANDROID_ROOT/frameworks/base/opengl/include \
576        KHR/khrplatform.h
577fi
578
579# API level 5
580if platform_check 5; then
581    copy_system_shared_library libGLESv2
582    copy_system_headers $ANDROID_ROOT/frameworks/base/opengl/include \
583        GLES2/gl2.h \
584        GLES2/gl2ext.h \
585        GLES2/gl2platform.h
586fi
587
588# API level 8
589if platform_check 8; then
590    copy_system_shared_library libandroid
591    copy_system_shared_library libjnigraphics
592    copy_system_headers $ANDROID_ROOT/frameworks/base/native/include \
593        android/bitmap.h
594fi
595
596# API level 9
597if platform_check 9; then
598    case $ARCH in
599    arm)
600        copy_system_object_file crtbegin_so
601        copy_system_object_file crtend_so
602        ;;
603    esac
604
605    copy_system_shared_library libandroid
606    copy_system_headers $ANDROID_ROOT/frameworks/base/native/include \
607        android/asset_manager.h \
608        android/asset_manager_jni.h \
609        android/configuration.h \
610        android/input.h \
611        android/keycodes.h \
612        android/looper.h \
613        android/native_activity.h \
614        android/native_window.h \
615        android/native_window_jni.h \
616        android/obb.h \
617        android/rect.h \
618        android/sensor.h \
619        android/storage_manager.h \
620        android/window.h
621
622    copy_system_shared_library libEGL
623    copy_system_headers $ANDROID_ROOT/frameworks/base/opengl/include \
624        EGL/egl.h \
625        EGL/eglext.h \
626        EGL/eglplatform.h
627
628    set_symbol_excludes '^_' '^MPH_' # remove MPH_to_xxx definitions
629    copy_system_shared_library libOpenSLES
630    copy_system_headers $ANDROID_ROOT/system/media/wilhelm/include \
631        SLES/OpenSLES.h \
632        SLES/OpenSLES_Android.h \
633        SLES/OpenSLES_AndroidConfiguration.h \
634        SLES/OpenSLES_AndroidMetadata.h \
635        SLES/OpenSLES_Platform.h
636fi
637
638# API level 14
639if platform_check 14; then
640    set_symbol_excludes '^_' '^MPH_' # remove MPH_to_xxx definitions
641    copy_system_shared_library libOpenMAXAL
642    copy_system_headers $ANDROID_ROOT/system/media/wilhelm/include \
643        OMXAL/OpenMAXAL.h \
644        OMXAL/OpenMAXAL_Android.h \
645        OMXAL/OpenMAXAL_Platform.h
646fi
647
648clear_symbol_excludes
649
650dump "Done!"
651