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#
84BEGIN {
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
102END {
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#
160function 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
183function 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