# Copyright (C) 2011 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script is used to generate a shell script that will be # run by the NDK build system to process dependency files generated by # GCC on Windows, and convert them to a format that is suitable for # Cygwin's GNU Make. # # The main issue to solve here is that the dependency files generated # by GCC use native windows path names, as in: # # C:/Foo/foo.o: \ # C:/Foo/src/foo.h \ # C:/Foo/src/foo.c \ # D:/Bar/bar/bar.h # # And the file needs to be processed to convert each such path into # a Cygwin-specific one, as in: # # /cygdrive/c/Foo/foo.o: \ # /cygdrive/c/Foo/src/foo.h \ # /cygdrive/c/Foo/src/foo.c \ # /cygdrive/d/Bar/bar/bar.h # # Previously, this conversion was done with an awk script that assumed # that the cygwin drive prefix was always 'cygdrive'. This didn't work # well when this was not the case, or when using drive-less mounts # (e.g. when /home/mnt would map to //server/subdir) # # To solve the issue correctly, we need to parse the output of the # Cygwin mount table (i.e. the output of the 'mount' command), and # build a sed script that will properly replace host paths into the # corresponding cygwin equivalent. # # NOTE: The sed script will be run during command execution, not during the # parse phase. # # This awk script expects its input to be the output of the Cygwin "mount" command # as in: # # C:/cygwin/bin on /usr/bin type ntfs (binary,auto) # C:/cygwin/lib on /usr/lib type ntfs (binary,auto) # C:/cygwin on / type ntfs (binary,auto) # C: on /cygdrive/c type ntfs (binary,posix=0,user,noumount,auto) # D: on /cygdrive/d type udf (binary,posix=0,user,noumount,auto) # //server/subdir on /home/mnt.2$ type .... # # It first builds a sed script that convert all windows path in the # an input file into the cygwin equivalent. For example, this would look # like the following (but all on a single line): # # s!^//server/subdir!/home/mnt\.2\$!ig; # s! //server/subdir! /home/mnt\.2\$!ig; # s!^C:/cygwin/bin!/usr/bin!ig; # s! C:/cygwin/bin! /usr/bin!ig; # s!^C:/cygwin/lib!/usr/lib!ig; # s! C:/cygwin/lib! /usr/lib!ig; # s!^C:/cygwin/!/!ig; # s! C:/cygwin/! /!ig; # s!^C:!/cygdrive/c!ig; # s! C:! /cygdrive/c!ig; # s!^D:!/cygdrive/d!ig; # s! D:! /cygdrive/d!ig; # # Note that we properly escape regex meta characters like . or $ # to avoid confusing sed. Also deal with the cases where the path # is the first in the line, or prefixed with a space in the deps file. # # After this, the sed invokation is hard-coded into a generated shell # script that can be invoked directly at build time. # BEGIN { # setup our count count = 0 } $2 == "on" { # record a new (host-path,cygwin-path) pair count ++ # Convert backwards slashes into forward ones in the host path. # This is to support MSys' mount command, which outputs Windows-style # separators, unlike Cygwin's version of the same tool. gsub("\\\\","/",$1) host[count] = $1 cygwin[count] = $3 } END { # We have recorded all (host,cygwin) path pairs, # now try to sort them so that the ones with the longest host path # appear first for (ii = 2; ii <= count; ii++) { for (jj = ii-1; jj > 0; jj--) { if (length(host[jj]) > length(host[jj+1])) { break; } if (length(host[jj]) == length(host[jj+1]) && host[jj] > host[jj+1]) { break } tmp = cygwin[jj] cygwin[jj] = cygwin[jj+1] cygwin[jj+1] = tmp tmp = host[jj] host[jj] = host[jj+1] host[jj+1] = tmp } } # build/core/init.mk defines VERBOSE to 1 when it needs to dump the # list of substitutions in a human-friendly format, generally when # NDK_LOG is defined in the environment # # Otherwise, just generate the corresponding sed script # if (VERBOSE == 1) { for (nn = 1; nn <= count; nn++) { printf( "$(info %s => %s)", cygwin[nn], host[nn]); } } else { RESULT = "" for (nn = 1; nn <= count; nn++) { add_drive_rule(host[nn], cygwin[nn]) } # Note: the role of the generated shell script is to first check # that $1.org exists. If this is not the case, this simply # means that GCC didn't generate a depedency file (e.g. when # compiling an assembler file). # # If the file exists, it is processed with our sed script, # the output is written to $1, and we remove the original $1.org # print "#!/bin/sh" print "# AUTO-GENERATED FILE, DO NOT EDIT!" print "if [ -f $1.org ]; then" print " sed -e '" RESULT "' $1.org > $1 && rm -f $1.org" print "fi" } } # We need to quote some characters so that 'sed' doesn't # believe they are regex operators. For example, if a path # contains a dot (.), we need to escape it into "\." # function sed_quote_path (str) { # Windows path names cannot contain any of: <>:"|?* # see msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx # # Anything else is valid. The regex meta characters are: ^.[]$()|*+?{}\ # # This means we need to escape these when they appear in path names: ^.[]$()+{}\ # gsub("\\^","\\^",str) gsub("\\.","\\.",str) gsub("\\[","\\[",str) gsub("\\]","\\]",str) gsub("\\$","\\$",str) gsub("\\(","\\(",str) gsub("\\)","\\)",str) gsub("\\+","\\+",str) gsub("\\{","\\{",str) gsub("\\}","\\}",str) return str } function add_drive_rule (hostpath,cygpath) { hostpath = sed_quote_path(hostpath) cygpath = sed_quote_path(cygpath) # The root directory is a special case, because we need # to add a slash at the end of the corresponding host path # otherwise c:/cygwin/foo will be translated into //foo # instead of /foo. # if (cygpath == "/") { hostpath = hostpath "/" } # when the hostpath starts the line RESULT = RESULT "s!^" hostpath "!" cygpath "!ig;" # when the hostpath does not start the line (it will always be after a space) RESULT = RESULT "s! " hostpath "! " cygpath "!ig;" }