|
1 #============================================================================ |
|
2 #Name : builders.py |
|
3 #Part of : Helium |
|
4 |
|
5 #Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
6 #All rights reserved. |
|
7 #This component and the accompanying materials are made available |
|
8 #under the terms of the License "Eclipse Public License v1.0" |
|
9 #which accompanies this distribution, and is available |
|
10 #at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
11 # |
|
12 #Initial Contributors: |
|
13 #Nokia Corporation - initial contribution. |
|
14 # |
|
15 #Contributors: |
|
16 # |
|
17 #Description: |
|
18 #=============================================================================== |
|
19 |
|
20 """ |
|
21 This modules contains the archive builders. |
|
22 An archive builder is a class that is able to use data from a configuration |
|
23 and generate a set of shell commands. |
|
24 """ |
|
25 import archive.tools |
|
26 import archive.selectors |
|
27 import archive.mappers |
|
28 import archive.scanners |
|
29 import logging |
|
30 import pathaddition.relative |
|
31 import buildtools |
|
32 import os |
|
33 import codecs |
|
34 import fileutils |
|
35 |
|
36 _logger = logging.getLogger('archive') |
|
37 _logger.setLevel(logging.INFO) |
|
38 |
|
39 class ArchivePreBuilder(buildtools.PreBuilder): |
|
40 """ Processes an archive build specification. """ |
|
41 def __init__(self, config_set, config_name, writerType='ant', index = None): |
|
42 buildtools.PreBuilder.__init__(self, config_set) |
|
43 self.configs = config_set.getConfigurations() |
|
44 self.spec_name = config_name |
|
45 self.index = index |
|
46 self.writerType = writerType |
|
47 self.listToFindPrefix = [] |
|
48 |
|
49 def build_manifest(self, config): |
|
50 """ Generate a manifest file from the a configuration. """ |
|
51 _logger.info('Processing archive config: ' + config['name']) |
|
52 _scanners = archive.scanners.get_scanners(config.get_list('scanners', ['default']), config) |
|
53 |
|
54 content_list = {} |
|
55 |
|
56 if not os.path.exists(config['temp.build.dir']): |
|
57 os.makedirs(config['temp.build.dir']) |
|
58 manifest_file_path = os.path.abspath(os.path.join(config['temp.build.dir'], config['name'] + '_includefile.txt')) |
|
59 out = codecs.open(manifest_file_path, 'w+', 'utf-8') |
|
60 |
|
61 # zip.root.dir can be set to root.dir so that when zipping from another dir, |
|
62 # the manifest is relative to that dir |
|
63 (_, root_dir) = os.path.splitdrive(os.path.normpath(config.get('zip.root.dir', config['root.dir']))) |
|
64 _logger.info(" * Scanning") |
|
65 for scanner in _scanners: |
|
66 _logger.debug("Scanner %s" % scanner) |
|
67 for subpath in scanner.scan(): |
|
68 (_, subpath) = os.path.splitdrive(subpath) |
|
69 if pathaddition.relative.abs2rel(subpath, root_dir): |
|
70 _logger.debug(subpath) |
|
71 subpath = subpath[len(root_dir):] |
|
72 if subpath.startswith(os.sep): |
|
73 subpath = subpath[1:] |
|
74 # normpath is to remove any occurances of "..\.." before checking for duplicates |
|
75 subpath = os.path.normpath(subpath) |
|
76 if subpath not in content_list: |
|
77 out.write(u"".join([subpath, u'\n'])) |
|
78 content_list[subpath] = True |
|
79 |
|
80 out.close() |
|
81 return manifest_file_path |
|
82 |
|
83 def manifest_to_commands(self, config, manifest): |
|
84 """ Generate return a command list. Commands are stored in a two dimension array.""" |
|
85 _logger.info(" * Generating commands") |
|
86 tool = archive.tools.get_tool(config['archive.tool']) |
|
87 mapper_name = 'default' |
|
88 if config.has_key('mapper'): |
|
89 mapper_name = config['mapper'] |
|
90 mapper = archive.mappers.get_mapper(mapper_name, config, tool) |
|
91 return mapper.create_commands(manifest) |
|
92 |
|
93 def create_command_list(self, commands): |
|
94 """ Convert a two dimensions array of command to a CommandList object. """ |
|
95 stages = buildtools.CommandList() |
|
96 newstage = False |
|
97 for cmds_stage in commands: |
|
98 _logger.debug("Stage: %s" % cmds_stage) |
|
99 for cmd in cmds_stage: |
|
100 stages.addCommand(cmd, newstage) |
|
101 newstage = False |
|
102 newstage = True |
|
103 return stages |
|
104 |
|
105 def writeTopLevel(self, build_file_path, output_path, xml_file): |
|
106 """Creates a build tool config makefile that executes archieve build.""" |
|
107 config_name_list = [] |
|
108 for config in self.configs: |
|
109 config_name_list.append(config['name']) |
|
110 if not os.path.exists(config['archives.dir']): |
|
111 os.makedirs(config['archives.dir']) |
|
112 |
|
113 writer = buildtools.get_writer(self.writerType, build_file_path) |
|
114 writer.writeTopLevel(config_name_list, self.spec_name, output_path, xml_file) |
|
115 writer.close() |
|
116 |
|
117 def getCommonUncRoots(self, uncPaths): |
|
118 """get common UNC roots""" |
|
119 commonRoots = {} |
|
120 for p_path in uncPaths: |
|
121 commonRoots["\\\\" + os.sep.join(p_path[2:].split(os.sep)[0:2]) + os.sep] = 1 |
|
122 return commonRoots.keys() |
|
123 |
|
124 def getPrefix(self, dir, commonUncRoots): |
|
125 """get prefix""" |
|
126 for root in commonUncRoots: |
|
127 if dir.startswith(root): |
|
128 return root |
|
129 raise Exception("Could not find root for %s." % dir) |
|
130 |
|
131 def checkRootDirValue(self, builder, parse_xml_file, build_drive, config_type): |
|
132 """Checks UNC path in root.dir and adds the substituted drive into EMAKEROOT.""" |
|
133 substDrives = [] |
|
134 if build_drive: |
|
135 substDrives.append(build_drive + os.sep) |
|
136 |
|
137 # Read all the config's root.dir to get UNC Path if any |
|
138 # Of course this is only on windows platform |
|
139 if os.sep == '\\': |
|
140 for config in self.configs: |
|
141 (drive, root_dir) = os.path.splitdrive(os.path.normpath(config['root.dir'])) |
|
142 _logger.debug("drive=%s, root_dir=%s" % (drive, root_dir)) |
|
143 if drive == "": |
|
144 self.listToFindPrefix.append(root_dir) |
|
145 |
|
146 commonUncRoots = self.getCommonUncRoots(self.listToFindPrefix) |
|
147 _logger.debug("Common roots %s" % (commonUncRoots)) |
|
148 driveMapping = {} |
|
149 for root in commonUncRoots: |
|
150 _logger.info("Substing %s" % (root)) |
|
151 driveMapping[root] = self.substUncPath(root) |
|
152 _logger.debug("%s subst as %s" % (root, driveMapping[root])) |
|
153 substDrives.append(driveMapping[root] + os.sep) |
|
154 |
|
155 for config in self.configs: |
|
156 (drive, root_dir) = os.path.splitdrive(os.path.normpath(config['root.dir']) + os.sep) |
|
157 if drive == "": |
|
158 for root in driveMapping: |
|
159 if root_dir.startswith(root): |
|
160 config['root.dir'] = os.path.normpath(driveMapping[root] + os.sep + root_dir[len(root):len(root_dir)]) |
|
161 _logger.info("Updated %s in %s" % (root_dir, config['root.dir'])) |
|
162 config['unsubst.dir'] = driveMapping[root] |
|
163 break |
|
164 elif drive != build_drive: |
|
165 if config['root.dir'] not in substDrives: |
|
166 substDrives.append(config['root.dir']) |
|
167 else: |
|
168 for config in self.configs: |
|
169 if config['root.dir'].startswith('\\\\'): |
|
170 _logger.error("UNC path are not supported under this platform: %s" % (config['root.dir'])) |
|
171 builder.writeToXML(parse_xml_file, self.configs, config_type) |
|
172 return os.path.pathsep.join(substDrives) |
|
173 |
|
174 |
|
175 def substUncPath(self, path): |
|
176 """substitute UNC path""" |
|
177 freedrive = fileutils.get_next_free_drive() |
|
178 fileutils.subst(freedrive, path) |
|
179 return freedrive |
|
180 |
|
181 def cleanupSubstDrives(self): |
|
182 """ Read all the config's root.dir to get UNC Path if any""" |
|
183 drives = {} |
|
184 for config in self.configs: |
|
185 _logger.debug("Checking configuration...") |
|
186 _logger.debug("unsubst.dir: %s" % 'unsubst.dir' in config) |
|
187 _logger.debug("drives: %s" % drives) |
|
188 if 'unsubst.dir' in config and not config['unsubst.dir'] in drives: |
|
189 _logger.debug("Found drive to unsubst %s" % (config['unsubst.dir'])) |
|
190 self.unSubStituteDrives(config['unsubst.dir']) |
|
191 drives[config['unsubst.dir']] = config['unsubst.dir'] |
|
192 |
|
193 def unSubStituteDrives(self, drive): |
|
194 """un-substitute Drives""" |
|
195 _logger.info("Unsubsting %s" % (drive)) |
|
196 fileutils.unsubst(drive) |
|
197 |
|
198 def write(self, outputname): |
|
199 """Creates a build tool configuration file that executes archive build operations. |
|
200 |
|
201 The input to each archive build operation is an includefile that lists |
|
202 all the files to be included in the archive. These text files are |
|
203 generated before the build file by scanning the filesystem. |
|
204 """ |
|
205 stages = buildtools.CommandList() |
|
206 |
|
207 commands = [] |
|
208 if self.index > len(self.configs): |
|
209 raise Exception("index not found in configuration") |
|
210 config = self.configs[self.index] |
|
211 stages = self.manifest_to_commands(config, self.build_manifest(config)) |
|
212 |
|
213 # merging the commands |
|
214 while len(commands) < len(stages): |
|
215 commands.append([]) |
|
216 for i in range(len(stages)): |
|
217 commands[i].extend(stages[i]) |
|
218 |
|
219 writer = buildtools.get_writer(self.writerType, outputname) |
|
220 writer.write(self.create_command_list(commands)) |
|
221 writer.close() |