1#!/bin/sh 2########################################################################### 3# 4# Window Maker window manager 5# 6# Copyright (c) 2014 Christophe CURIS 7# Copyright (c) 2014 Window Maker Team 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License along 20# with this program; if not, write to the Free Software Foundation, Inc., 21# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 22# 23########################################################################### 24# 25# generate-mapfile-from-header.sh: 26# from a list of C header files, extract the name of the functions and 27# global variables that are considered public for a library, and generate 28# a 'version script' for ld with that list, so that all other symbols 29# will be considered local. 30# 31# The goal is that the symbol that are not part of the public API should 32# not be visible to the user because it can be a source of problems (from 33# name clash to evolutivity issues). 34# 35########################################################################### 36# 37# Please note that this script is writen in sh+awk on purpose: this script 38# is gonna be run on the machine of the person who is trying to compile 39# WindowMaker, and as such we cannot be sure to find any scripting language 40# in a known version and that works (python/ruby/tcl/perl/php/you-name-it). 41# 42# So for portability, we stick to the same sh+awk constraint as Autotools 43# to limit the problem, see for example: 44# http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Portable-Shell.html 45# 46########################################################################### 47 48# Report an error on stderr and exit with status 1 to tell make could not work 49arg_error() { 50 echo "$0: $@" >&2 51 exit 1 52} 53 54# print help and exit with success status 55print_help() { 56 echo "$0: generate a script for ld to keep only the symbols from C header" 57 echo "Usage: $0 [options...] input_file[s]" 58 echo "valid options are:" 59 echo " -l : add symbol's definition line number for debug" 60 echo " -n name : name to use for the library" 61 echo " -v version : set the library version for ld" 62 exit 0 63} 64 65# Extract command line arguments 66while [ $# -gt 0 ]; do 67 case $1 in 68 -l) 69 debug_info=' /* " symbol_type " at " FILENAME ":" NR " */' 70 ;; 71 72 -n) 73 shift 74 echo "$1" | grep '^[A-Za-z][A-Za-z_0-9]*$' > /dev/null || arg_error "invalid name \"$1\" for a library" 75 libname="$1" 76 ;; 77 78 -v) 79 shift 80 # Version may be 'x:y:z', we keep only 'x' 81 version="`echo "$1" | sed -e 's,:.*$,,' `" 82 # the version should only be a number 83 echo "$version" | grep '^[1-9][0-9]*$' > /dev/null || \ 84 arg_error "version \"$1\" is not valid" 85 ;; 86 87 -h|-help|--help) print_help ;; 88 -*) arg_error "unknow option '$1'" ;; 89 90 *) 91 [ -r "$1" ] || arg_error "source file \"$1\" is not readable" 92 input_files="$input_files $1" 93 ;; 94 esac 95 shift 96done 97 98# Check consistency of command-line 99[ "x$input_files" = "x" ] && arg_error "no source file given" 100 101# Automatic values 102if [ -z "$libname" ]; then 103 # build the library name from the first header name, remove path stuff, extension and invalid characters 104 libname="`echo "$input_files" | sed -e 's,^ ,,;s, .*$,,;s,^.*/\([^/]*\)$,\1,;s,\.[^.]*$,,;s,[^A-Za-z_0-9],_,g;s,^_*,,' `" 105fi 106 107# Parse the input file and find the function declarations; extract the names 108# and place them in the 'global' section so that ld will keep the symbols; 109# generate the rest of the script so that other symbols will not be kept. 110awk ' 111BEGIN { 112 print "/* Generated version-script for ld */"; 113 print ""; 114 print "'"$libname$version"'"; 115 print "{"; 116 print " global:"; 117} 118 119/^#[ \t]*define/ { 120 # Ignore macro definition because their content could be mis-interpreted 121 while (/\\$/) { 122 if (getline == 0) { break; } 123 } 124 next; 125} 126 127/\/\*/ { 128 # Remove comments to avoid mis-detection 129 pos = index($0, "/*"); 130 comment = substr($0, pos + 2); 131 $0 = substr($0, 1, pos - 1); 132 while (1) { 133 pos = index(comment, "*/"); 134 if (pos > 0) { break; } 135 getline comment; 136 } 137 $0 = $0 substr(comment, pos + 2); 138} 139 140/extern[ \t].*;/ { 141 line = $0; 142 143 # Remove everything from the ";" 144 sub(/;.*$/, "", line); 145 146 # Check if it is a global variable 147 nb = split(line, words, /[\t *]/); 148 149 valid = 1; 150 for (i = 1; i < nb; i++) { 151 # If the word looks valid, do not abort 152 if (words[i] ~ /^[A-Za-z_0-9]*$/) { continue; } 153 154 # Treat the case were the variable is an array, and there was space(s) after the name 155 if (words[i] ~ /^\[/) { nb = i - 1; break; } 156 157 # If we are here, the word is not valid; we stop processing the line here but we do 158 # not use "next" so the line may match function prototype handler below 159 valid = 0; 160 break; 161 } 162 163 if (valid) { 164 # Remove array size, if any 165 sub(/\[.*$/, "", words[nb]); 166 167 if (words[nb] ~ /^[A-Za-z][A-Za-z_0-9]*$/) { 168 symbol_type = "variable"; 169 print " " words[nb] ";'"$debug_info"'"; 170 next; 171 } 172 } 173} 174 175/^[ \t]*typedef/ { 176 # Skip type definition because they could be mis-interpreted when it 177 # defines a prototype for a callback function 178 nb_braces = 0; 179 while (1) { 180 # Count the number of brace pairs to skip the content of the definition 181 nb = split($0, dummy_array, /\{/); 182 nb_braces = nb_braces + nb; 183 184 nb = split($0, dummy_array, /\}/); 185 nb_braces = nb_braces - nb; 186 187 # Stop when we have out count of pair of braces and there is the final ";" 188 if ((nb_braces == 0) && (dummy_array[nb] ~ /;/)) { break; } 189 190 if (getline == 0) { 191 break; 192 } 193 } 194 next; 195} 196 197/[\t ]\**[A-Z_a-z][A-Z_a-z0-9]*[\t ]*\(/ { 198 # Get everything until the end of the definition, to avoid mis-interpreting 199 # arguments and attributes on next lines 200 while (1) { 201 pos = index($0, ";"); 202 if (pos != 0) { break; } 203 204 getline nxt; 205 $0 = $0 nxt; 206 } 207 208 # Remove the argument list 209 sub(/[ \t]*\(.*$/, ""); 210 211 # Trim possible indentation at beginning of line 212 sub(/^[\t ]*/, ""); 213 214 # If it does not start by a keyword, it is probably not a valid function 215 if (!/^[A-Z_a-z]/) { next; } 216 217 nb = split($0, words, /[\t *]/); 218 for (i = 1; i < nb; i++) { 219 # Do not consider "static" because it is used for functions to be inlined 220 if (words[i] == "static") { next; } 221 222 # Allow empty keyword, that were just "*" 223 if (words[i] == "") { continue; } 224 225 # If it does not match, it is unlikely to be a function 226 if (words[i] !~ /^[A-Z_a-z][A-Z_a-z0-9]*$/) { next; } 227 } 228 229 # If we arrived so far, everything looks valid and the last argument of 230 # the array contains the name of the function 231 symbol_type = "function"; 232 print " " words[nb] ";'"$debug_info"'"; 233} 234 235END { 236 print ""; 237 print " local:"; 238 print " *;"; 239 print "};"; 240} 241' $input_files 242