Home | History | Annotate | Download | only in awk
      1 # Copyright (C) 2011 The Android Open Source Project
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #      http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 #
     15 
     16 # This script is used to generate a shell script that will be
     17 # run by the NDK build system to process dependency files generated by
     18 # GCC on Windows, and convert them to a format that is suitable for
     19 # Cygwin's GNU Make.
     20 #
     21 # The main issue to solve here is that the dependency files generated
     22 # by GCC use native windows path names, as in:
     23 #
     24 #    C:/Foo/foo.o: \
     25 #      C:/Foo/src/foo.h \
     26 #      C:/Foo/src/foo.c \
     27 #      D:/Bar/bar/bar.h
     28 #
     29 # And the file needs to be processed to convert each such path into
     30 # a Cygwin-specific one, as in:
     31 #
     32 #    /cygdrive/c/Foo/foo.o: \
     33 #      /cygdrive/c/Foo/src/foo.h \
     34 #      /cygdrive/c/Foo/src/foo.c \
     35 #      /cygdrive/d/Bar/bar/bar.h
     36 #
     37 # Previously, this conversion was done with an awk script that assumed
     38 # that the cygwin drive prefix was always 'cygdrive'. This didn't work
     39 # well when this was not the case, or when using drive-less mounts
     40 # (e.g. when  /home/mnt would map to //server/subdir)
     41 #
     42 # To solve the issue correctly, we need to parse the output of the
     43 # Cygwin mount table (i.e. the output of the 'mount' command), and
     44 # build a sed script that will properly replace host paths into the
     45 # corresponding cygwin equivalent.
     46 #
     47 # NOTE: The sed script will be run during command execution, not during the
     48 # parse phase.
     49 #
     50 # This awk script expects its input to be the output of the Cygwin "mount" command
     51 # as in:
     52 #
     53 #  C:/cygwin/bin on /usr/bin type ntfs (binary,auto)
     54 #  C:/cygwin/lib on /usr/lib type ntfs (binary,auto)
     55 #  C:/cygwin on / type ntfs (binary,auto)
     56 #  C: on /cygdrive/c type ntfs (binary,posix=0,user,noumount,auto)
     57 #  D: on /cygdrive/d type udf (binary,posix=0,user,noumount,auto)
     58 #  //server/subdir on /home/mnt.2$ type ....
     59 #
     60 # It first builds a sed script that convert all windows path in the
     61 # an input file into the cygwin equivalent. For example, this would look
     62 # like the following (but all on a single line):
     63 #
     64 #  s!^//server/subdir!/home/mnt\.2\$!ig;
     65 #  s! //server/subdir! /home/mnt\.2\$!ig;
     66 #  s!^C:/cygwin/bin!/usr/bin!ig;
     67 #  s! C:/cygwin/bin! /usr/bin!ig;
     68 #  s!^C:/cygwin/lib!/usr/lib!ig;
     69 #  s! C:/cygwin/lib! /usr/lib!ig;
     70 #  s!^C:/cygwin/!/!ig;
     71 #  s! C:/cygwin/! /!ig;
     72 #  s!^C:!/cygdrive/c!ig;
     73 #  s! C:! /cygdrive/c!ig;
     74 #  s!^D:!/cygdrive/d!ig;
     75 #  s! D:! /cygdrive/d!ig;
     76 #
     77 # Note that we properly escape regex meta characters like . or $
     78 # to avoid confusing sed. Also deal with the cases where the path
     79 # is the first in the line, or prefixed with a space in the deps file.
     80 #
     81 # After this, the sed invokation is hard-coded into a generated shell
     82 # script that can be invoked directly at build time.
     83 #
     84 BEGIN {
     85   # setup our count
     86   count = 0
     87 }
     88 
     89 $2 == "on" {
     90     # record a new (host-path,cygwin-path) pair
     91     count ++
     92 
     93     # Convert backwards slashes into forward ones in the host path.
     94     # This is to support MSys' mount command, which outputs Windows-style
     95     # separators, unlike Cygwin's version of the same tool.
     96     gsub("\\\\","/",$1)
     97 
     98     host[count] = $1
     99     cygwin[count] = $3
    100 }
    101 
    102 END {
    103     # We have recorded all (host,cygwin) path pairs,
    104     # now try to sort them so that the ones with the longest host path
    105     # appear first
    106     for (ii = 2; ii <= count; ii++) {
    107         for (jj = ii-1; jj > 0; jj--) {
    108             if (length(host[jj]) > length(host[jj+1])) {
    109                 break;
    110             }
    111             if (length(host[jj]) == length(host[jj+1]) &&
    112                 host[jj] > host[jj+1]) {
    113                 break
    114             }
    115             tmp = cygwin[jj]
    116             cygwin[jj] = cygwin[jj+1]
    117             cygwin[jj+1] = tmp
    118             tmp = host[jj]
    119             host[jj] = host[jj+1]
    120             host[jj+1] = tmp
    121         }
    122     }
    123 
    124     # build/core/init.mk defines VERBOSE to 1 when it needs to dump the
    125     # list of substitutions in a human-friendly format, generally when
    126     # NDK_LOG is defined in the environment
    127     #
    128     # Otherwise, just generate the corresponding sed script
    129     #
    130     if (VERBOSE == 1) {
    131         for (nn = 1; nn <= count; nn++) {
    132             printf( "$(info %s => %s)", cygwin[nn], host[nn]);
    133         }
    134     } else {
    135         RESULT = ""
    136         for (nn = 1; nn <= count; nn++) {
    137             add_drive_rule(host[nn], cygwin[nn])
    138         }
    139 
    140         # Note: the role of the generated shell script is to first check
    141         #       that $1.org exists. If this is not the case, this simply
    142         #       means that GCC didn't generate a depedency file (e.g. when
    143         #       compiling an assembler file).
    144         #
    145         #       If the file exists, it is processed with our sed script,
    146         #       the output is written to $1, and we remove the original $1.org
    147         #
    148         print "#!/bin/sh"
    149         print "# AUTO-GENERATED FILE, DO NOT EDIT!"
    150         print "if [ -f $1.org ]; then"
    151         print "  sed -e '" RESULT "' $1.org > $1 && rm -f $1.org"
    152         print "fi"
    153     }
    154 }
    155 
    156 # We need to quote some characters so that 'sed' doesn't
    157 # believe they are regex operators. For example, if a path
    158 # contains a dot (.), we need to escape it into "\."
    159 #
    160 function sed_quote_path (str)
    161 {
    162     # Windows path names cannot contain any of: <>:"|?*
    163     # see msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
    164     #
    165     # Anything else is valid. The regex meta characters are: ^.[]$()|*+?{}\
    166     #
    167     # This means we need to escape these when they appear in path names: ^.[]$()+{}\
    168     #
    169     gsub("\\^","\\^",str)
    170     gsub("\\.","\\.",str)
    171     gsub("\\[","\\[",str)
    172     gsub("\\]","\\]",str)
    173     gsub("\\$","\\$",str)
    174     gsub("\\(","\\(",str)
    175     gsub("\\)","\\)",str)
    176     gsub("\\+","\\+",str)
    177     gsub("\\{","\\{",str)
    178     gsub("\\}","\\}",str)
    179 
    180     return str
    181 }
    182 
    183 function add_drive_rule (hostpath,cygpath)
    184 {
    185     hostpath = sed_quote_path(hostpath)
    186 	cygpath = sed_quote_path(cygpath)
    187 	
    188     # The root directory is a special case, because we need
    189 	# to add a slash at the end of the corresponding host path
    190 	# otherwise c:/cygwin/foo will be translated into //foo
    191 	# instead of /foo.
    192 	#
    193     if (cygpath == "/") {
    194         hostpath = hostpath "/"
    195     }
    196 	# when the hostpath starts the line
    197     RESULT = RESULT "s!^" hostpath "!" cygpath "!ig;"
    198 
    199 	# when the hostpath does not start the line (it will always be after a space)
    200 	RESULT = RESULT "s! " hostpath "! " cygpath "!ig;"
    201 }
    202