Import BaseMemoryLibMmx;
[mirror_edk2.git] / Tools / Python / MkFar.py
1 #!/usr/bin/env python
2
3 """This is a python script that takes user input from the command line and
4 creates a far (Framework Archive Manifest) file for distribution."""
5
6 import os, sys, getopt, string, xml.dom.minidom, zipfile, md5
7 from XmlRoutines import *
8 from WorkspaceRoutines import *
9
10 class Far:
11 """This class is used to collect arbitrarty data from the template file."""
12 def __init__(far):
13 """Assign the default values for the far fields."""
14 far.FileName = "output.far"
15 far.FarName=""
16 far.Version=""
17 far.License=""
18 far.Abstract=""
19 far.Description=""
20 far.Copyright=""
21 far.SpdFiles=[]
22 far.FpdFiles=[]
23 far.ExtraFiles=[]
24
25 far = Far()
26 """The far object is constructed from the template file the user passed in."""
27
28 def AddToZip(zip, infile):
29
30 """Add a file to a zip file, provided it is not already there."""
31
32 if not infile in zip.namelist():
33 zip.write(inWorkspace(infile), infile)
34
35 def parseMsa(msaFile, spdDir):
36
37 """Parse an msa file and return a list of all the files that this msa
38 includes."""
39
40 filelist = [msaFile]
41
42 msaDir = os.path.dirname(msaFile)
43
44 msa = xml.dom.minidom.parse(inWorkspace(os.path.join(spdDir, msaFile)))
45
46 xmlPaths = [
47 "/ModuleSurfaceArea/SourceFiles/Filename",
48 "/ModuleSurfaceArea/NonProcessedFiles/Filename" ]
49
50 for xmlPath in xmlPaths:
51 for f in XmlList(msa, xmlPath):
52 filelist.append(str(os.path.join(msaDir, XmlElementData(f))))
53
54 return filelist
55
56 def parseSpd(spdFile):
57
58 """Parse an spd file and return a list of all the files that this spd
59 includes."""
60
61 files = []
62
63 spdDir = os.path.dirname(spdFile)
64
65 spd = xml.dom.minidom.parse(inWorkspace(spdFile))
66
67 # We are currently ignoring these hints.
68 readonly = XmlElement(spd, "/PackageSurfaceArea/PackageDefinitions/ReadOnly") != "false"
69 repackage = XmlElement(spd, "/PackageSurfaceArea/PackageDefinitions/RePackage") != "false"
70
71 xmlPaths = [
72 "/PackageSurfaceArea/LibraryClassDeclarations/LibraryClass/IncludeHeader",
73 "/PackageSurfaceArea/IndustryStdIncludes/IndustryStdHeader/IncludeHeader" ]
74
75 # These are covered by the Industry Standard Includes.
76 # "/PackageSurfaceArea/PackageHeaders/IncludePkgHeader"
77
78 for xmlPath in xmlPaths:
79 for f in XmlList(spd, xmlPath):
80 files.append(str(XmlElementData(f)))
81
82 for f in XmlList(spd, "/PackageSurfaceArea/MsaFiles/Filename"):
83 msaFile = str(XmlElementData(f))
84 files += parseMsa(msaFile, spdDir)
85
86 cwd = os.getcwd()
87 os.chdir(inWorkspace(spdDir))
88 for root, dirs, entries in os.walk("Include"):
89 # Some files need to be skipped.
90 for r in ["CVS", ".svn"]:
91 if r in dirs:
92 dirs.remove(r)
93 for entry in entries:
94 files.append(os.path.join(os.path.normpath(root), entry))
95 os.chdir(cwd)
96
97 return files
98
99 def makeFarHeader(doc):
100
101 """Create a dom tree for the Far Header. It will use information from the
102 template file passed on the command line, if present."""
103
104 header = XmlAppendChildElement(doc.documentElement, "FarHeader")
105
106 XmlAppendChildElement(header, "FarName", far.FarName)
107 XmlAppendChildElement(header, "GuidValue", genguid())
108 XmlAppendChildElement(header, "Version", far.Version)
109 XmlAppendChildElement(header, "Abstract", far.Abstract)
110 XmlAppendChildElement(header, "Description", far.Description)
111 XmlAppendChildElement(header, "Copyright", far.Copyright)
112 XmlAppendChildElement(header, "License", far.License)
113 XmlAppendChildElement(header, "Specification", "FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052")
114
115 return header
116
117 def getSpdGuidVersion(spdFile):
118
119 """Returns a tuple (guid, version) which is read from the given spdFile."""
120
121 spd = xml.dom.minidom.parse(inWorkspace(spdFile))
122
123 return (XmlElement(spd, "/PackageSurfaceArea/SpdHeader/GuidValue"),
124 XmlElement(spd, "/PackageSurfaceArea/SpdHeader/Version"))
125
126 def makeFar(files, farname):
127
128 """Make a far out of the given filelist and writes it to the file farname."""
129
130 domImpl = xml.dom.minidom.getDOMImplementation()
131 man = domImpl.createDocument(None, "FrameworkArchiveManifest", None)
132 top_element = man.documentElement
133
134 top_element.appendChild(makeFarHeader(man))
135
136 packList = XmlAppendChildElement(top_element, "FarPackageList")
137 platList = XmlAppendChildElement(top_element, "FarPlatformList")
138 contents = XmlAppendChildElement(top_element, "Contents")
139 XmlAppendChildElement(top_element, "UserExtensions")
140
141 try:
142 zip = zipfile.ZipFile(farname, "w", zipfile.ZIP_DEFLATED)
143 except:
144 zip = zipfile.ZipFile(farname, "w", zipfile.ZIP_STORED)
145 for infile in set(files):
146 if not os.path.exists(inWorkspace(infile)):
147 print "Error: Non-existent file '%s'." % infile
148 sys.exit()
149 (_, extension) = os.path.splitext(infile)
150 if extension == ".spd":
151 filelist = parseSpd(infile)
152 spdDir = os.path.dirname(infile)
153
154 (spdGuid, spdVersion) = getSpdGuidVersion(infile)
155
156 package = XmlAppendChildElement(packList, "FarPackage")
157 XmlAppendChildElement(package, "FarFilename", lean(infile), {"Md5Sum": Md5(inWorkspace(infile))})
158 AddToZip(zip, infile)
159 XmlAppendChildElement(package, "GuidValue", spdGuid)
160 XmlAppendChildElement(package, "Version", spdVersion)
161 XmlAppendChildElement(package, "DefaultPath", spdDir)
162 XmlAppendChildElement(package, "FarPlatformList")
163 packContents = XmlAppendChildElement(package, "Contents")
164 XmlAppendChildElement(package, "UserExtensions")
165
166 for spdfile in filelist:
167 XmlAppendChildElement(packContents, "FarFilename", lean(spdfile), {"Md5Sum": Md5(inWorkspace(os.path.join(spdDir, spdfile)))})
168 AddToZip(zip, os.path.join(spdDir,spdfile))
169
170 elif extension == ".fpd":
171
172 platform = XmlAppendChildElement(platList, "FarPlatform")
173 XmlAppendChildElement(platform, "FarFilename", lean(infile), {"Md5Sum": Md5(inWorkspace(infile))})
174 AddToZip(zip, infile)
175
176 else:
177 XmlAppendChildElement(contents, "FarFilename", lean(infile), {"Md5Sum": Md5(inWorkspace(infile))})
178 AddToZip(zip, infile)
179
180 zip.writestr("FrameworkArchiveManifest.xml", man.toxml('UTF-8'))
181 zip.close()
182 return
183
184 # This acts like the main() function for the script, unless it is 'import'ed
185 # into another script.
186 if __name__ == '__main__':
187
188 # Create a pretty printer for dumping data structures in a readable form.
189 # pp = pprint.PrettyPrinter(indent=2)
190
191 # Process the command line args.
192 optlist, args = getopt.getopt(sys.argv[1:], 'ho:t:v', [ 'template=', 'output=', 'far=', 'help', 'debug', 'verbose', 'version'])
193
194 # First pass through the options list.
195 for o, a in optlist:
196 if o in ["-h", "--help"]:
197 print """
198 Pass a list of .spd and .fpd files to be placed into a far for distribution.
199 You may give the name of the far with a -f or --far option. For example:
200
201 %s --template far-template --far library.far MdePkg/MdePkg.spd
202
203 The file paths of .spd and .fpd are treated as relative to the WORKSPACE
204 environment variable which must be set to a valid workspace root directory.
205
206 A template file may be passed in with the --template option. This template file
207 is a text file that allows more contol over the contents of the far.
208 """ % os.path.basename(sys.argv[0])
209
210 sys.exit()
211 optlist.remove((o,a))
212 if o in ["-t", "--template"]:
213 # The template file is processed first, so that command line options can
214 # override it.
215 templateName = a
216 execfile(templateName)
217 optlist.remove((o,a))
218
219 # Second pass through the options list. These can override the first pass.
220 for o, a in optlist:
221 if o in ["-o", "--far", "--output"]:
222 far.FileName = a
223
224 # Let's err on the side of caution and not let people blow away data
225 # accidentally.
226 if os.path.exists(far.FileName):
227 print "Error: File %s exists. Not overwriting." % far.FileName
228 sys.exit()
229
230 makeFar(far.SpdFiles + far.FpdFiles + far.ExtraFiles + args, far.FileName)