OLD | NEW |
1 """scons.Node.FS | 1 """scons.Node.FS |
2 | 2 |
3 File system nodes. | 3 File system nodes. |
4 | 4 |
5 These Nodes represent the canonical external objects that people think | 5 These Nodes represent the canonical external objects that people think |
6 of when they think of building software: files and directories. | 6 of when they think of building software: files and directories. |
7 | 7 |
8 This holds a "default_fs" variable that should be initialized with an FS | 8 This holds a "default_fs" variable that should be initialized with an FS |
9 that can be used by scripts or modules looking for the canonical default. | 9 that can be used by scripts or modules looking for the canonical default. |
10 | 10 |
11 """ | 11 """ |
12 | 12 |
13 # | 13 # |
14 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundat
ion | 14 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons F
oundation |
15 # | 15 # |
16 # Permission is hereby granted, free of charge, to any person obtaining | 16 # Permission is hereby granted, free of charge, to any person obtaining |
17 # a copy of this software and associated documentation files (the | 17 # a copy of this software and associated documentation files (the |
18 # "Software"), to deal in the Software without restriction, including | 18 # "Software"), to deal in the Software without restriction, including |
19 # without limitation the rights to use, copy, modify, merge, publish, | 19 # without limitation the rights to use, copy, modify, merge, publish, |
20 # distribute, sublicense, and/or sell copies of the Software, and to | 20 # distribute, sublicense, and/or sell copies of the Software, and to |
21 # permit persons to whom the Software is furnished to do so, subject to | 21 # permit persons to whom the Software is furnished to do so, subject to |
22 # the following conditions: | 22 # the following conditions: |
23 # | 23 # |
24 # The above copyright notice and this permission notice shall be included | 24 # The above copyright notice and this permission notice shall be included |
25 # in all copies or substantial portions of the Software. | 25 # in all copies or substantial portions of the Software. |
26 # | 26 # |
27 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY | 27 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
28 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | 28 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
29 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | 29 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
30 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | 30 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
31 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | 31 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
32 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | 32 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
33 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 33 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
34 # | 34 # |
35 | 35 |
36 __revision__ = "src/engine/SCons/Node/FS.py 3842 2008/12/20 22:59:52 scons" | 36 __revision__ = "src/engine/SCons/Node/FS.py 3897 2009/01/13 06:45:54 scons" |
37 | 37 |
| 38 from itertools import izip |
| 39 import cStringIO |
38 import fnmatch | 40 import fnmatch |
39 from itertools import izip | |
40 import os | 41 import os |
41 import os.path | 42 import os.path |
42 import re | 43 import re |
43 import shutil | 44 import shutil |
44 import stat | 45 import stat |
45 import string | 46 import string |
46 import sys | 47 import sys |
47 import time | 48 import time |
48 import cStringIO | 49 |
| 50 try: |
| 51 import codecs |
| 52 except ImportError: |
| 53 pass |
| 54 else: |
| 55 # TODO(2.2): Remove when 2.3 becomes the minimal supported version. |
| 56 try: |
| 57 codecs.BOM_UTF8 |
| 58 except AttributeError: |
| 59 codecs.BOM_UTF8 = '\xef\xbb\xbf' |
| 60 try: |
| 61 codecs.BOM_UTF16 |
| 62 except AttributeError: |
| 63 if sys.byteorder == 'little': |
| 64 codecs.BOM_UTF16 = '\xff\xfe' |
| 65 else: |
| 66 codecs.BOM_UTF16 = '\xfe\xff' |
49 | 67 |
50 import SCons.Action | 68 import SCons.Action |
51 from SCons.Debug import logInstanceCreation | 69 from SCons.Debug import logInstanceCreation |
52 import SCons.Errors | 70 import SCons.Errors |
53 import SCons.Memoize | 71 import SCons.Memoize |
54 import SCons.Node | 72 import SCons.Node |
55 import SCons.Node.Alias | 73 import SCons.Node.Alias |
56 import SCons.Subst | 74 import SCons.Subst |
57 import SCons.Util | 75 import SCons.Util |
58 import SCons.Warnings | 76 import SCons.Warnings |
(...skipping 810 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
869 a File at this point, so morph into one.""" | 887 a File at this point, so morph into one.""" |
870 self.__class__ = File | 888 self.__class__ = File |
871 self._morph() | 889 self._morph() |
872 self.clear() | 890 self.clear() |
873 return File.rfile(self) | 891 return File.rfile(self) |
874 | 892 |
875 def scanner_key(self): | 893 def scanner_key(self): |
876 return self.get_suffix() | 894 return self.get_suffix() |
877 | 895 |
878 def get_contents(self): | 896 def get_contents(self): |
879 """Fetch the contents of the entry. | 897 """Fetch the contents of the entry. Returns the exact binary |
880 | 898 contents of the file.""" |
881 Since this should return the real contents from the file | |
882 system, we check to see into what sort of subclass we should | |
883 morph this Entry.""" | |
884 try: | 899 try: |
885 self = self.disambiguate(must_exist=1) | 900 self = self.disambiguate(must_exist=1) |
886 except SCons.Errors.UserError: | 901 except SCons.Errors.UserError: |
887 # There was nothing on disk with which to disambiguate | 902 # There was nothing on disk with which to disambiguate |
888 # this entry. Leave it as an Entry, but return a null | 903 # this entry. Leave it as an Entry, but return a null |
889 # string so calls to get_contents() in emitters and the | 904 # string so calls to get_contents() in emitters and the |
890 # like (e.g. in qt.py) don't have to disambiguate by hand | 905 # like (e.g. in qt.py) don't have to disambiguate by hand |
891 # or catch the exception. | 906 # or catch the exception. |
892 return '' | 907 return '' |
893 else: | 908 else: |
894 return self.get_contents() | 909 return self.get_contents() |
895 | 910 |
| 911 def get_text_contents(self): |
| 912 """Fetch the decoded text contents of a Unicode encoded Entry. |
| 913 |
| 914 Since this should return the text contents from the file |
| 915 system, we check to see into what sort of subclass we should |
| 916 morph this Entry.""" |
| 917 try: |
| 918 self = self.disambiguate(must_exist=1) |
| 919 except SCons.Errors.UserError: |
| 920 # There was nothing on disk with which to disambiguate |
| 921 # this entry. Leave it as an Entry, but return a null |
| 922 # string so calls to get_text_contents() in emitters and |
| 923 # the like (e.g. in qt.py) don't have to disambiguate by |
| 924 # hand or catch the exception. |
| 925 return '' |
| 926 else: |
| 927 return self.get_text_contents() |
| 928 |
896 def must_be_same(self, klass): | 929 def must_be_same(self, klass): |
897 """Called to make sure a Node is a Dir. Since we're an | 930 """Called to make sure a Node is a Dir. Since we're an |
898 Entry, we can morph into one.""" | 931 Entry, we can morph into one.""" |
899 if self.__class__ is not klass: | 932 if self.__class__ is not klass: |
900 self.__class__ = klass | 933 self.__class__ = klass |
901 self._morph() | 934 self._morph() |
902 self.clear() | 935 self.clear() |
903 | 936 |
904 # The following methods can get called before the Taskmaster has | 937 # The following methods can get called before the Taskmaster has |
905 # had a chance to call disambiguate() directly to see if this Entry | 938 # had a chance to call disambiguate() directly to see if this Entry |
(...skipping 20 matching lines...) Expand all Loading... |
926 | 959 |
927 def new_ninfo(self): | 960 def new_ninfo(self): |
928 return self.disambiguate().new_ninfo() | 961 return self.disambiguate().new_ninfo() |
929 | 962 |
930 def changed_since_last_build(self, target, prev_ni): | 963 def changed_since_last_build(self, target, prev_ni): |
931 return self.disambiguate().changed_since_last_build(target, prev_ni) | 964 return self.disambiguate().changed_since_last_build(target, prev_ni) |
932 | 965 |
933 def _glob1(self, pattern, ondisk=True, source=False, strings=False): | 966 def _glob1(self, pattern, ondisk=True, source=False, strings=False): |
934 return self.disambiguate()._glob1(pattern, ondisk, source, strings) | 967 return self.disambiguate()._glob1(pattern, ondisk, source, strings) |
935 | 968 |
| 969 def get_subst_proxy(self): |
| 970 return self.disambiguate().get_subst_proxy() |
| 971 |
936 # This is for later so we can differentiate between Entry the class and Entry | 972 # This is for later so we can differentiate between Entry the class and Entry |
937 # the method of the FS class. | 973 # the method of the FS class. |
938 _classEntry = Entry | 974 _classEntry = Entry |
939 | 975 |
940 | 976 |
941 class LocalFS: | 977 class LocalFS: |
942 | 978 |
943 if SCons.Memoize.use_memoizer: | 979 if SCons.Memoize.use_memoizer: |
944 __metaclass__ = SCons.Memoize.Memoized_Metaclass | 980 __metaclass__ = SCons.Memoize.Memoized_Metaclass |
945 | 981 |
(...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1591 | 1627 |
1592 def alter_targets(self): | 1628 def alter_targets(self): |
1593 """Return any corresponding targets in a variant directory. | 1629 """Return any corresponding targets in a variant directory. |
1594 """ | 1630 """ |
1595 return self.fs.variant_dir_target_climb(self, self, []) | 1631 return self.fs.variant_dir_target_climb(self, self, []) |
1596 | 1632 |
1597 def scanner_key(self): | 1633 def scanner_key(self): |
1598 """A directory does not get scanned.""" | 1634 """A directory does not get scanned.""" |
1599 return None | 1635 return None |
1600 | 1636 |
| 1637 def get_text_contents(self): |
| 1638 """We already emit things in text, so just return the binary |
| 1639 version.""" |
| 1640 return self.get_contents() |
| 1641 |
1601 def get_contents(self): | 1642 def get_contents(self): |
1602 """Return content signatures and names of all our children | 1643 """Return content signatures and names of all our children |
1603 separated by new-lines. Ensure that the nodes are sorted.""" | 1644 separated by new-lines. Ensure that the nodes are sorted.""" |
1604 contents = [] | 1645 contents = [] |
1605 name_cmp = lambda a, b: cmp(a.name, b.name) | 1646 name_cmp = lambda a, b: cmp(a.name, b.name) |
1606 sorted_children = self.children()[:] | 1647 sorted_children = self.children()[:] |
1607 sorted_children.sort(name_cmp) | 1648 sorted_children.sort(name_cmp) |
1608 for node in sorted_children: | 1649 for node in sorted_children: |
1609 contents.append('%s %s\n' % (node.get_csig(), node.name)) | 1650 contents.append('%s %s\n' % (node.get_csig(), node.name)) |
1610 return string.join(contents, '') | 1651 return string.join(contents, '') |
1611 | 1652 |
1612 def get_csig(self): | 1653 def get_csig(self): |
1613 """Compute the content signature for Directory nodes. In | 1654 """Compute the content signature for Directory nodes. In |
1614 general, this is not needed and the content signature is not | 1655 general, this is not needed and the content signature is not |
1615 stored in the DirNodeInfo. However, if get_contents on a Dir | 1656 stored in the DirNodeInfo. However, if get_contents on a Dir |
1616 node is called which has a child directory, the child | 1657 node is called which has a child directory, the child |
1617 directory should return the hash of its contents.""" | 1658 directory should return the hash of its contents.""" |
(...skipping 611 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2229 self.changed_since_last_build = self.decide_target | 2270 self.changed_since_last_build = self.decide_target |
2230 | 2271 |
2231 def scanner_key(self): | 2272 def scanner_key(self): |
2232 return self.get_suffix() | 2273 return self.get_suffix() |
2233 | 2274 |
2234 def get_contents(self): | 2275 def get_contents(self): |
2235 if not self.rexists(): | 2276 if not self.rexists(): |
2236 return '' | 2277 return '' |
2237 fname = self.rfile().abspath | 2278 fname = self.rfile().abspath |
2238 try: | 2279 try: |
2239 r = open(fname, "rb").read() | 2280 contents = open(fname, "rb").read() |
2240 except EnvironmentError, e: | 2281 except EnvironmentError, e: |
2241 if not e.filename: | 2282 if not e.filename: |
2242 e.filename = fname | 2283 e.filename = fname |
2243 raise | 2284 raise |
2244 return r | 2285 return contents |
| 2286 |
| 2287 try: |
| 2288 import codecs |
| 2289 except ImportError: |
| 2290 get_text_contents = get_contents |
| 2291 else: |
| 2292 # This attempts to figure out what the encoding of the text is |
| 2293 # based upon the BOM bytes, and then decodes the contents so that |
| 2294 # it's a valid python string. |
| 2295 def get_text_contents(self): |
| 2296 contents = self.get_contents() |
| 2297 if contents.startswith(codecs.BOM_UTF8): |
| 2298 contents = contents.decode('utf-8') |
| 2299 elif contents.startswith(codecs.BOM_UTF16): |
| 2300 contents = contents.decode('utf-16') |
| 2301 return contents |
2245 | 2302 |
2246 def get_content_hash(self): | 2303 def get_content_hash(self): |
2247 """ | 2304 """ |
2248 Compute and return the MD5 hash for this file. | 2305 Compute and return the MD5 hash for this file. |
2249 """ | 2306 """ |
2250 if not self.rexists(): | 2307 if not self.rexists(): |
2251 return SCons.Util.MD5signature('') | 2308 return SCons.Util.MD5signature('') |
2252 fname = self.rfile().abspath | 2309 fname = self.rfile().abspath |
2253 try: | 2310 try: |
2254 cs = SCons.Util.MD5filesignature(fname, | 2311 cs = SCons.Util.MD5filesignature(fname, |
(...skipping 572 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2827 result = self | 2884 result = self |
2828 if not self.exists(): | 2885 if not self.exists(): |
2829 norm_name = _my_normcase(self.name) | 2886 norm_name = _my_normcase(self.name) |
2830 for dir in self.dir.get_all_rdirs(): | 2887 for dir in self.dir.get_all_rdirs(): |
2831 try: node = dir.entries[norm_name] | 2888 try: node = dir.entries[norm_name] |
2832 except KeyError: node = dir.file_on_disk(self.name) | 2889 except KeyError: node = dir.file_on_disk(self.name) |
2833 if node and node.exists() and \ | 2890 if node and node.exists() and \ |
2834 (isinstance(node, File) or isinstance(node, Entry) \ | 2891 (isinstance(node, File) or isinstance(node, Entry) \ |
2835 or not node.is_derived()): | 2892 or not node.is_derived()): |
2836 result = node | 2893 result = node |
| 2894 # Copy over our local attributes to the repository |
| 2895 # Node so we identify shared object files in the |
| 2896 # repository and don't assume they're static. |
| 2897 # |
| 2898 # This isn't perfect; the attribute would ideally |
| 2899 # be attached to the object in the repository in |
| 2900 # case it was built statically in the repository |
| 2901 # and we changed it to shared locally, but that's |
| 2902 # rarely the case and would only occur if you |
| 2903 # intentionally used the same suffix for both |
| 2904 # shared and static objects anyway. So this |
| 2905 # should work well in practice. |
| 2906 result.attributes = self.attributes |
2837 break | 2907 break |
2838 self._memo['rfile'] = result | 2908 self._memo['rfile'] = result |
2839 return result | 2909 return result |
2840 | 2910 |
2841 def rstr(self): | 2911 def rstr(self): |
2842 return str(self.rfile()) | 2912 return str(self.rfile()) |
2843 | 2913 |
2844 def get_cachedir_csig(self): | 2914 def get_cachedir_csig(self): |
2845 """ | 2915 """ |
2846 Fetch a Node's content signature for purposes of computing | 2916 Fetch a Node's content signature for purposes of computing |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3066 try: | 3136 try: |
3067 entry.clear_memoized_values() | 3137 entry.clear_memoized_values() |
3068 except AttributeError: | 3138 except AttributeError: |
3069 # Not a Node object, try to look up Node by filename. XXX | 3139 # Not a Node object, try to look up Node by filename. XXX |
3070 # This creates Node objects even for those filenames which | 3140 # This creates Node objects even for those filenames which |
3071 # do not correspond to an existing Node object. | 3141 # do not correspond to an existing Node object. |
3072 node = get_default_fs().Entry(entry) | 3142 node = get_default_fs().Entry(entry) |
3073 if node: | 3143 if node: |
3074 node.clear_memoized_values() | 3144 node.clear_memoized_values() |
3075 | 3145 |
OLD | NEW |