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