]> git.proxmox.com Git - ceph.git/blame - ceph/src/zstd/contrib/single_file_libs/combine.sh
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / zstd / contrib / single_file_libs / combine.sh
CommitLineData
f67539c2
TL
1#!/bin/sh -e
2
3# Tool to bundle multiple C/C++ source files, inlining any includes.
4#
5# Note: this POSIX-compliant script is many times slower than the original bash
6# implementation (due to the grep calls) but it runs and works everywhere.
7#
8# TODO: ROOTS, FOUND, etc., as arrays (since they fail on paths with spaces)
9# TODO: revert to Bash-only regex (the grep ones being too slow)
10#
11# Author: Carl Woffenden, Numfum GmbH (this script is released under a CC0 license/Public Domain)
12
13# Common file roots
14ROOTS="."
15
16# -x option excluded includes
17XINCS=""
18
19# -k option includes to keep as include directives
20KINCS=""
21
22# Files previously visited
23FOUND=""
24
25# Optional destination file (empty string to write to stdout)
26DESTN=""
27
28# Whether the "#pragma once" directives should be written to the output
29PONCE=0
30
31# Prints the script usage then exits
32usage() {
33 echo "Usage: $0 [-r <path>] [-x <header>] [-k <header>] [-o <outfile>] infile"
34 echo " -r file root search path"
35 echo " -x file to completely exclude from inlining"
36 echo " -k file to exclude from inlining but keep the include directive"
37 echo " -p keep any '#pragma once' directives (removed by default)"
38 echo " -o output file (otherwise stdout)"
39 echo "Example: $0 -r ../my/path - r ../other/path -o out.c in.c"
40 exit 1
41}
42
43# Tests that the grep implementation works as expected (older OSX grep fails)
44test_deps() {
45 if ! echo '#include "foo"' | grep -Eq '^\s*#\s*include\s*".+"'; then
46 echo "Aborting: the grep implementation fails to parse include lines"
47 exit 1
48 fi
49 if ! echo '"foo.h"' | sed -E 's/"([^"]+)"/\1/' | grep -Eq '^foo\.h$'; then
50 echo "Aborting: sed is unavailable or non-functional"
51 exit 1
52 fi
53}
54
55# Tests if list $1 has item $2 (returning zero on a match)
56list_has_item() {
57 if echo "$1" | grep -Eq "(^|\s*)$2(\$|\s*)"; then
58 return 0
59 else
60 return 1
61 fi
62}
63
64# Adds a new line with the supplied arguments to $DESTN (or stdout)
65write_line() {
66 if [ -n "$DESTN" ]; then
67 printf '%s\n' "$@" >> "$DESTN"
68 else
69 printf '%s\n' "$@"
70 fi
71}
72
73log_line() {
74 echo $@ >&2
75}
76
77# Find this file!
78resolve_include() {
79 local srcdir=$1
80 local inc=$2
81 for root in $srcdir $ROOTS; do
82 if [ -f "$root/$inc" ]; then
83 # Try to reduce the file path into a canonical form (so that multiple)
84 # includes of the same file are successfully deduplicated, even if they
85 # are expressed differently.
86 local relpath="$(realpath --relative-to . "$root/$inc" 2>/dev/null)"
87 if [ "$relpath" != "" ]; then # not all realpaths support --relative-to
88 echo "$relpath"
89 return 0
90 fi
91 local relpath="$(realpath "$root/$inc" 2>/dev/null)"
92 if [ "$relpath" != "" ]; then # not all distros have realpath...
93 echo "$relpath"
94 return 0
95 fi
96 # Fallback on Python to reduce the path if the above fails.
97 local relpath=$(python -c "import os,sys; print os.path.relpath(sys.argv[1])" "$root/$inc" 2>/dev/null)
98 if [ "$relpath" != "" ]; then # not all distros have realpath...
99 echo "$relpath"
100 return 0
101 fi
102 # Worst case, fall back to just the root + relative include path. The
103 # problem with this is that it is possible to emit multiple different
104 # resolved paths to the same file, depending on exactly how its included.
105 # Since the main loop below keeps a list of the resolved paths it's
106 # already included, in order to avoid repeated includes, this failure to
107 # produce a canonical/reduced path can lead to multiple inclusions of the
108 # same file. But it seems like the resulting single file library still
109 # works (hurray include guards!), so I guess it's ok.
110 echo "$root/$inc"
111 return 0
112 fi
113 done
114 return 1
115}
116
117# Adds the contents of $1 with any of its includes inlined
118add_file() {
119 local file=$1
120 if [ -n "$file" ]; then
121 log_line "Processing: $file"
122 # Get directory of the current so we can resolve relative includes
123 local srcdir="$(dirname "$file")"
124 # Read the file
125 local line=
126 while IFS= read -r line; do
127 if echo "$line" | grep -Eq '^\s*#\s*include\s*".+"'; then
128 # We have an include directive so strip the (first) file
129 local inc=$(echo "$line" | grep -Eo '".*"' | sed -E 's/"([^"]+)"/\1/' | head -1)
130 local res_inc="$(resolve_include "$srcdir" "$inc")"
131 if list_has_item "$XINCS" "$inc"; then
132 # The file was excluded so error if the source attempts to use it
133 write_line "#error Using excluded file: $inc"
134 log_line "Excluding: $inc"
135 else
136 if ! list_has_item "$FOUND" "$res_inc"; then
137 # The file was not previously encountered
138 FOUND="$FOUND $res_inc"
139 if list_has_item "$KINCS" "$inc"; then
140 # But the include was flagged to keep as included
141 write_line "/**** *NOT* inlining $inc ****/"
142 write_line "$line"
143 log_line "Not Inlining: $inc"
144 else
145 # The file was neither excluded nor seen before so inline it
146 write_line "/**** start inlining $inc ****/"
147 add_file "$res_inc"
148 write_line "/**** ended inlining $inc ****/"
149 fi
150 else
151 write_line "/**** skipping file: $inc ****/"
152 fi
153 fi
154 else
155 # Skip any 'pragma once' directives, otherwise write the source line
156 local write=$PONCE
157 if [ $write -eq 0 ]; then
158 if echo "$line" | grep -Eqv '^\s*#\s*pragma\s*once\s*'; then
159 write=1
160 fi
161 fi
162 if [ $write -ne 0 ]; then
163 write_line "$line"
164 fi
165 fi
166 done < "$file"
167 else
168 write_line "#error Unable to find \"$1\""
169 log_line "Error: Unable to find: \"$1\""
170 fi
171}
172
173while getopts ":r:x:k:po:" opts; do
174 case $opts in
175 r)
176 ROOTS="$ROOTS $OPTARG"
177 ;;
178 x)
179 XINCS="$XINCS $OPTARG"
180 ;;
181 k)
182 KINCS="$KINCS $OPTARG"
183 ;;
184 p)
185 PONCE=1
186 ;;
187 o)
188 DESTN="$OPTARG"
189 ;;
190 *)
191 usage
192 ;;
193 esac
194done
195shift $((OPTIND-1))
196
197if [ -n "$1" ]; then
198 if [ -f "$1" ]; then
199 if [ -n "$DESTN" ]; then
200 printf "" > "$DESTN"
201 fi
202 test_deps
203 add_file "$1"
204 else
205 echo "Input file not found: \"$1\""
206 exit 1
207 fi
208else
209 usage
210fi
211exit 0