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 961 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
972 """Takes a dict |env| whose values are strings that can refer to other keys, | 972 """Takes a dict |env| whose values are strings that can refer to other keys, |
973 for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of | 973 for example env['foo'] = '$(bar) and $(baz)'. Returns a list L of all keys of |
974 env such that key2 is after key1 in L if env[key2] refers to env[key1]. | 974 env such that key2 is after key1 in L if env[key2] refers to env[key1]. |
975 | 975 |
976 Throws an Exception in case of dependency cycles. | 976 Throws an Exception in case of dependency cycles. |
977 """ | 977 """ |
978 # Since environment variables can refer to other variables, the evaluation | 978 # Since environment variables can refer to other variables, the evaluation |
979 # order is important. Below is the logic to compute the dependency graph | 979 # order is important. Below is the logic to compute the dependency graph |
980 # and sort it. | 980 # and sort it. |
981 regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}') | 981 regex = re.compile(r'\$\{([a-zA-Z0-9\-_]+)\}') |
982 | 982 def GetEdges(node): |
983 # First sort the list of keys. | 983 # Use a definition of edges such that user_of_variable -> used_varible. |
984 key_list = sorted(env.keys()) | 984 # This happens to be easier in this case, since a variable's |
985 | 985 # definition contains all variables it references in a single string. |
986 # Phase 1: Create a set of edges of (DEPENDEE, DEPENDER) where in the graph, | 986 # We can then reverse the result of the topological sort at the end. |
987 # DEPENDEE -> DEPENDER. Also create sets of dependers and dependees. | 987 # Since: reverse(topsort(DAG)) = topsort(reverse_edges(DAG)) |
988 edges = set() | 988 matches = set([v for v in regex.findall(env[node]) if v in env]) |
989 dependees = set() | |
990 dependers = set() | |
991 for k in key_list: | |
992 matches = regex.findall(env[k]) | |
993 if not len(matches): | |
994 continue | |
995 | |
996 depends_on_other_var = False | |
997 for dependee in matches: | 989 for dependee in matches: |
998 assert '${' not in dependee, 'Nested variables not supported: ' + dependee | 990 assert '${' not in dependee, 'Nested variables not supported: ' + dependee |
999 if dependee in env: | 991 return matches |
1000 edges.add((dependee, k)) | |
1001 dependees.add(dependee) | |
1002 depends_on_other_var = True | |
1003 if depends_on_other_var: | |
1004 dependers.add(k) | |
1005 | 992 |
1006 # Phase 2: Create a list of graph nodes with no incoming edges. | 993 try: |
1007 sorted_nodes = [] | 994 # Topologically sort, and then reverse, because we used an edge definition |
1008 edgeless_nodes = dependees - dependers | 995 # that's inverted from the expected result of this function (see comment |
1009 | 996 # above). |
1010 # Phase 3: Perform Kahn topological sort. | 997 order = gyp.common.TopologicallySorted(env.keys(), GetEdges) |
1011 while len(edgeless_nodes): | 998 order.reverse() |
1012 # Find a node with no incoming edges, add it to the sorted list, and | 999 return order |
1013 # remove it from the list of nodes that aren't part of the graph. | 1000 except CycleError, e: |
1014 node = edgeless_nodes.pop() | 1001 raise Exception( |
1015 sorted_nodes.append(node) | 1002 'Xcode environment variables are cyclically dependent: ' + str(e.nodes)) |
1016 key_list.remove(node) | |
1017 | |
1018 # Find all the edges between |node| and other nodes. | |
1019 edges_to_node = [e for e in edges if e[0] == node] | |
1020 for edge in edges_to_node: | |
1021 edges.remove(edge) | |
1022 # If the node connected to |node| by |edge| has no other incoming edges, | |
1023 # add it to |edgeless_nodes|. | |
1024 if not len([e for e in edges if e[1] == edge[1]]): | |
1025 edgeless_nodes.add(edge[1]) | |
1026 | |
1027 # Any remaining edges indicate a cycle. | |
1028 if len(edges): | |
1029 raise Exception('Xcode environment variables are cyclically dependent: ' + | |
1030 str(edges)) | |
1031 | |
1032 # Append the "nodes" not in the graph to those that were just sorted. | |
1033 sorted_nodes.extend(key_list) | |
1034 | |
1035 return sorted_nodes | |
1036 | 1003 |
1037 | 1004 |
1038 def GetSpecPostbuildCommands(spec, quiet=False): | 1005 def GetSpecPostbuildCommands(spec, quiet=False): |
1039 """Returns the list of postbuilds explicitly defined on |spec|, in a form | 1006 """Returns the list of postbuilds explicitly defined on |spec|, in a form |
1040 executable by a shell.""" | 1007 executable by a shell.""" |
1041 postbuilds = [] | 1008 postbuilds = [] |
1042 for postbuild in spec.get('postbuilds', []): | 1009 for postbuild in spec.get('postbuilds', []): |
1043 if not quiet: | 1010 if not quiet: |
1044 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( | 1011 postbuilds.append('echo POSTBUILD\\(%s\\) %s' % ( |
1045 spec['target_name'], postbuild['postbuild_name'])) | 1012 spec['target_name'], postbuild['postbuild_name'])) |
1046 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) | 1013 postbuilds.append(gyp.common.EncodePOSIXShellList(postbuild['action'])) |
1047 return postbuilds | 1014 return postbuilds |
OLD | NEW |