OLD | NEW |
---|---|
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 This module contains classes that help to emulate xcodebuild behavior on top of | 6 This module contains classes that help to emulate xcodebuild behavior on top of |
7 other build systems, such as make and ninja. | 7 other build systems, such as make and ninja. |
8 """ | 8 """ |
9 | 9 |
10 import gyp.common | 10 import gyp.common |
(...skipping 950 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
961 str = str.replace(to_replace, '${' + variable + '}') | 961 str = str.replace(to_replace, '${' + variable + '}') |
962 | 962 |
963 return str | 963 return str |
964 | 964 |
965 | 965 |
966 def ExpandEnvVars(string, expansions): | 966 def ExpandEnvVars(string, expansions): |
967 """Expands ${VARIABLES}, $(VARIABLES), and $VARIABLES in string per the | 967 """Expands ${VARIABLES}, $(VARIABLES), and $VARIABLES in string per the |
968 expansions dict. If the variable expands to something that references | 968 expansions dict. If the variable expands to something that references |
969 another variable, this variable is expanded as well if it's in env -- | 969 another variable, this variable is expanded as well if it's in env -- |
970 until no variables present in env are left.""" | 970 until no variables present in env are left.""" |
971 for k in reversed(TopologicallySortedEnvVarKeys(expansions)): | 971 for k in TopologicallySortedEnvVarKeys(expansions): |
972 string = string.replace('${' + k + '}', expansions[k]) | 972 string = string.replace('${' + k + '}', expansions[k]) |
973 string = string.replace('$(' + k + ')', expansions[k]) | 973 string = string.replace('$(' + k + ')', expansions[k]) |
974 string = string.replace('$' + k, expansions[k]) | 974 string = string.replace('$' + k, expansions[k]) |
975 return string | 975 return string |
976 | 976 |
977 | 977 |
978 def TopologicallySortedEnvVarKeys(env): | 978 def TopologicallySortedEnvVarKeys(env): |
979 """Takes a dict |env| whose values are strings that can refer to other keys, | 979 """Takes a dict |env| whose values are strings that can refer to other keys, |
980 for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of | 980 for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of |
981 env such that key2 is after key1 in L if env[key2] refers to env[key1]. | 981 env such that key1 is after key2 in L if env[key2] refers to env[key1]. |
982 | 982 |
983 Throws an Exception in case of dependency cycles. | 983 Throws an Exception in case of dependency cycles. |
984 """ | 984 """ |
985 # Since environment variables can refer to other variables, the evaluation | 985 # Since environment variables can refer to other variables, the evaluation |
986 # order is important. Below is the logic to compute the dependency graph | 986 # order is important. Below is the logic to compute the dependency graph |
987 # and sort it. | 987 # and sort it. |
988 regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}') | 988 regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}') |
989 | 989 def GetEdges(node): |
Nico
2012/05/11 14:53:44
This function is the wrong way round. We want an e
bradn
2012/05/11 18:57:23
Done.
| |
990 # First sort the list of keys. | 990 # Get all variable references for variable defined in env. |
991 key_list = sorted(env.keys()) | 991 matches = set([v for v in regex.findall(env[node]) if v in env]) |
992 | |
993 # Phase 1: Create a set of edges of (DEPENDEE, DEPENDER) where in the graph, | |
994 # DEPENDEE -> DEPENDER. Also create sets of dependers and dependees. | |
995 edges = set() | |
996 dependees = set() | |
997 dependers = set() | |
998 for k in key_list: | |
999 matches = regex.findall(env[k]) | |
1000 if not len(matches): | |
1001 continue | |
1002 | |
1003 depends_on_other_var = False | |
1004 for dependee in matches: | 992 for dependee in matches: |
1005 assert '${' not in dependee, 'Nested variables not supported: ' + dependee | 993 assert '${' not in dependee, 'Nested variables not supported: ' + dependee |
1006 if dependee in env: | 994 return matches |
1007 edges.add((dependee, k)) | |
1008 dependees.add(dependee) | |
1009 depends_on_other_var = True | |
1010 if depends_on_other_var: | |
1011 dependers.add(k) | |
1012 | 995 |
1013 # Phase 2: Create a list of graph nodes with no incoming edges. | 996 try: |
1014 sorted_nodes = [] | 997 return gyp.common.TopologicallySorted(env.keys(), GetEdges) |
1015 edgeless_nodes = dependees - dependers | 998 except CycleError, e: |
1016 | 999 raise Exception( |
1017 # Phase 3: Perform Kahn topological sort. | 1000 'Xcode environment variables are cyclically dependent: ' + str(e.nodes)) |
1018 while len(edgeless_nodes): | |
1019 # Find a node with no incoming edges, add it to the sorted list, and | |
1020 # remove it from the list of nodes that aren't part of the graph. | |
1021 node = edgeless_nodes.pop() | |
1022 sorted_nodes.append(node) | |
1023 key_list.remove(node) | |
1024 | |
1025 # Find all the edges between |node| and other nodes. | |
1026 edges_to_node = [e for e in edges if e[0] == node] | |
1027 for edge in edges_to_node: | |
1028 edges.remove(edge) | |
1029 # If the node connected to |node| by |edge| has no other incoming edges, | |
1030 # add it to |edgeless_nodes|. | |
1031 if not len([e for e in edges if e[1] == edge[1]]): | |
1032 edgeless_nodes.add(edge[1]) | |
1033 | |
1034 # Any remaining edges indicate a cycle. | |
1035 if len(edges): | |
1036 raise Exception('Xcode environment variables are cyclically dependent: ' + | |
1037 str(edges)) | |
1038 | |
1039 # Append the "nodes" not in the graph to those that were just sorted. | |
1040 sorted_nodes.extend(key_list) | |
1041 | |
1042 return sorted_nodes | |
1043 | 1001 |
1044 | 1002 |
1045 def GetSpecPostbuildCommands(spec, quiet=False): | 1003 def GetSpecPostbuildCommands(spec, quiet=False): |
1046 """Returns the list of postbuilds explicitly defined on |spec|, in a form | 1004 """Returns the list of postbuilds explicitly defined on |spec|, in a form |
1047 executable by a shell.""" | 1005 executable by a shell.""" |
1048 postbuilds = [] | 1006 postbuilds = [] |
1049 for postbuild in spec.get('postbuilds', []): | 1007 for postbuild in spec.get('postbuilds', []): |
1050 if not quiet: | 1008 if not quiet: |
1051 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( | 1009 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( |
1052 spec['target_name'], postbuild['postbuild_name'])) | 1010 spec['target_name'], postbuild['postbuild_name'])) |
1053 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) | 1011 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) |
1054 return postbuilds | 1012 return postbuilds |
OLD | NEW |