def generate_wxi(src, output_filename=None, id=None, diskId=None): add_wix_to_path() prefix = "analysis_tool_" while src[-1] in ('/', '\\'): src = src[:-1] name = os.path.basename(src) id = id or prefix + name.replace('-', '_').replace(' ', '_') output_filename = output_filename or _adjacent_file(id + ".wxi") import subprocess def check_call(args): print " ".join(args) subprocess.check_call(args) check_call(['heat', 'dir', _adjacent_file(src), '-template', 'fragment', '-sreg', '-scom', '-o', output_filename, '-ag', '-cg', id, '-srd', '-var', 'var.' + id, '-dr', id, '-nologo']) tree = ElementTree.parse(output_filename, parser=CommentedTreeBuilder()).getroot() tree.insert(0, ElementTree.Comment('generated with gen_analysis_tool_wxi.py %s\n' % src)) tree.insert(0, ElementTree.ProcessingInstruction('define', '%s=%s' % (id, os.path.normpath(src)))) parent_map = dict((c, p) for p in tree.getiterator() for c in p) for file in tree.findall(".//{http://schemas.microsoft.com/wix/2006/wi}Component/{http://schemas.microsoft.com/wix/2006/wi}File"): if file.get('Source', '').find('.svn') != -1: comp = parent_map[file] parent_map[comp].remove(comp) for dir in tree.findall(".//{http://schemas.microsoft.com/wix/2006/wi}Directory"): if dir.get('Name', '') == '.svn': for dirref in tree.findall(".//{http://schemas.microsoft.com/wix/2006/wi}DirectoryRef"): if dirref.get('Id', '') == dir.get('Id', ''): frag = parent_map[dirref] parent_map[frag].remove(frag) parent_map[dir].remove(dir) if diskId: for component in tree.findall(".//{http://schemas.microsoft.com/wix/2006/wi}Component"): component.attrib['DiskId'] = diskId # add registry nodes componentGroup = tree.findall(".//{http://schemas.microsoft.com/wix/2006/wi}ComponentGroup[@Id='" + id + "']")[0] componentRegistry = ElementTree.SubElement(componentGroup, 'ns0:Component', {'Id': id + '_RegistryEntry', 'Directory': id, 'Guid': '*', 'Win64': 'no'}) manifest = None with open(os.path.join(src, 'analysis_tool.manifest.json'), 'r') as f_p: manifest = json.load(f_p) for tool_name in manifest: registryKey = ElementTree.SubElement(componentRegistry, 'ns0:RegistryKey', {'Root': 'HKLM', 'Key': 'Software\\META\\AnalysisTools\\' + tool_name}) ElementTree.SubElement(registryKey, 'ns0:RegistryValue', {'Name': 'InstallLocation', 'Type': 'string', 'Value': '[INSTALLDIR]\\analysis_tools\\' + name}) ElementTree.SubElement(registryKey, 'ns0:RegistryValue', {'Name': 'Version', 'Type': 'string', 'Value': manifest[tool_name]['version']}) ElementTree.SubElement(registryKey, 'ns0:RegistryValue', {'Name': 'OutputDirectory', 'Type': 'string', 'Value': '[INSTALLDIR]\\analysis_tools\\' + name + '\\' + manifest[tool_name]['outputDirectory']}) ElementTree.SubElement(registryKey, 'ns0:RegistryValue', {'Name': 'RunCommand', 'Type': 'string', 'Value': manifest[tool_name]['runCommand']}) ElementTree.SubElement(registryKey, 'ns0:RegistryValue', {'Name': 'RequiredInterpreter', 'Type': 'string', 'Value': manifest[tool_name]['requiredInterpreter']}) ElementTree.ElementTree(tree).write(output_filename, xml_declaration=True) return name, id
def build_msi(offline, source_wxs='META_x64.wxs'): get_nuget_packages() generate_license_rtf() add_wix_to_path() def get_wixobj(file): return os.path.splitext(file)[0] + ".wixobj" def adjacent_file(file): return os.path.join(this_dir, file) gen_analysis_tool_wxi.main(r"..\analysis_tools", diskId='5') # gen_dir_from_vc: "explicit is better than implicit" # consider: generated files are left on disk after an svn switch, and get included in an installer that shouldn't have them gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\PCC\PCC", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\isis_meta\isis_meta", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\material_library\MaterialLibraryInterface", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\meta_nrmm\meta_nrmm", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\py_modelica\py_modelica", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\py_modelica_exporter\py_modelica_exporter", ) gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\cad_library\cad_library", ) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\run_mdao", ) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\testbenchexecutor", ) gen_dir_wxi.gen_dir_from_vc(r"..\meta\DesignDataPackage\lib\python", "DesignDataPackage_python.wxi", "DesignDataPackage_python") gen_dir_wxi.main(r"..\docs\_build\html") #gen_dir_wxi.main(r"CAD_Installs\Proe ISIS Extensions", "Proe_ISIS_Extensions_x64.wxi", "Proe_ISIS_Extensions_x64", diskId='4') # do not call gen_dir_from_vc, it would exclude CADCreoCreateAssembly.exe gen_dir_wxi.gen_dir_from_vc(r"..\meta\CyPhyML\icons", ) gen_dir_wxi.gen_dir_from_vc(r"..\models\Validation", ) import package_python bin_file_map = package_python.compileall() bin_file_map.update(package_python.zipall()) gen_dir_wxi.gen_dir_from_vc(r"..\bin", diskId='3', file_map=bin_file_map) bin_mods() gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\chipfit_display", ) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\layout_json", ) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\SpiceVisualizer") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\spice_viewer") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\cam2gerber") gen_dir_wxi.gen_dir_from_vc( r"..\src\Python27Packages\get_bom_with_eagle_xref") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\runCentroidUlp") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\runEagleUlp") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\runDrc") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\CADVisualizer") gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\get_eagle_path") def get_command_output(cmd): p = subprocess.Popen(cmd, stdout=subprocess.PIPE) out, err = p.communicate() return out.strip() def get_vcsversion(): p = subprocess.Popen("git rev-list HEAD --count".split(), stdout=subprocess.PIPE) out, err = p.communicate() return str(int(out.strip() or '651') - 650) vcsversion = get_vcsversion() def get_vcsdescribe(): out = get_command_output("git describe --match v*".split()) if out: return out # if there is no tag, use v0.1.1-[revision count]-g[short hash] return "v0.1.1-" + vcsversion + "-g" + get_command_output( "git rev-parse --short HEAD") def parse_describe(describe): m = re.match('^v(\\d+\\.\\d+\\.\\d+)((?:-[a-zA-Z_]+)?-(\\d+)-\\w+)?$', describe) if not m: raise ValueError( 'Invalid tag "{}". Must be in format v0.1.2 or v0.1.2-vendor'. format(describe)) return m.groups()[0] + '.' + (m.groups()[2] or '0') assert parse_describe('v0.16.1') == '0.16.1.0' assert parse_describe('v0.16.1-21-g57742becee') == '0.16.1.21' assert parse_describe('v0.16.1-vendor-21-g57742becee') == '0.16.1.21' vcsdescription = get_vcsdescribe() version = parse_describe(vcsdescription) print "Version description: " + vcsdescription print "Installer version: " + version sourcedir = os.path.relpath(this_dir) + '/' def get_githash(): p = subprocess.Popen("git rev-parse --short HEAD".split(), stdout=subprocess.PIPE) out, err = p.communicate() #if p.returncode: # raise subprocess.CalledProcessError(p.returncode, 'svnversion') return out.strip() or 'unknown' vcshash = get_githash() import glob sources_all = glob.glob(sourcedir + '*.wxi') + glob.glob(sourcedir + source_wxs) sources = [] include_wxis = [] # For each each ComponentGroupRef in "source_wxs" and "analysis_tools.wxi", # add its corresponding file to "include_wxis" for wxs in glob.glob(sourcedir + source_wxs) + glob.glob(sourcedir + 'analysis_tools.wxi'): print 'Processing WXS: ' + wxs tree = ET.parse(wxs) root = tree.getroot() #print root all_nodes = root.findall('.//') for node in all_nodes: if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentGroupRef': include_wxis.append(node.attrib['Id'] + '.wxi') include_wxis.append(node.attrib['Id'] + '_x64.wxi') if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentRef': include_wxis.append(node.attrib['Id'].rsplit(".", 1)[0] + '.wxi') include_wxis.append(node.attrib['Id'].rsplit(".", 1)[0] + '_x64.wxi') # For each file in include_wxis, check for ComponentGroupRef and ComponentRef. # Add any that you find index = 0 while index < len(include_wxis): wxi = include_wxis[index] index += 1 if not os.path.exists(wxi): continue tree = ET.parse(wxi) root = tree.getroot() all_nodes = root.findall('.//') for node in all_nodes: if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentGroupRef': include_wxis.append(node.attrib['Id'] + '.wxi') include_wxis.append(node.attrib['Id'] + '_x64.wxi') if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentRef': include_wxis.append(node.attrib['Id'].rsplit(".", 1)[0] + '.wxi') include_wxis.append(node.attrib['Id'].rsplit(".", 1)[0] + '_x64.wxi') include_wxis = set((wxi.lower() for wxi in include_wxis)) sources = [ source for source in sources_all if (os.path.basename(source).lower() in include_wxis) ] sources.append(source_wxs) if len(sources) == 0: raise Exception("0 sources found in " + sourcedir) defines = [('InterpreterBin', '../src/bin')] def get_mta_versions(mta_file): import uuid metaproject = win32com.client.Dispatch("MGA.MgaMetaProject") metaproject.Open('MGA=' + mta_file) try: return ("{" + str(uuid.UUID(bytes_le=metaproject.GUID)).upper() + "}", metaproject.Version) finally: metaproject.Close() cyphy_versions = get_mta_versions( adjacent_file('../generated/CyPhyML/models/CyPhyML.mta')) defines.append(('GUIDSTRCYPHYML', cyphy_versions[0])) defines.append(('VERSIONSTRCYPHYML', cyphy_versions[1])) defines.append(('VERSIONSTR', version)) defines.append(('VCSHASH', vcshash)) defines.append(('VCSDESCRIPTION', vcsdescription)) defines.append(('Compressed', ("yes" if offline else "no"))) from multiprocessing.pool import ThreadPool pool = ThreadPool() pool_exceptions = [] def candle(source): try: arch = ['-arch', ('x86' if source.find('x64') == -1 else 'x64')] system(['candle', '-ext', 'WiXUtilExtension'] + ['-d' + d[0] + '=' + d[1] for d in defines] + arch + ['-out', get_wixobj(source), source] + ['-nologo']) except Exception as e: pool_exceptions.append(sys.exc_info()) raise candle_results = pool.map_async(candle, sources, chunksize=1) pool.close() pool.join() if pool_exceptions: six.reraise(*pool_exceptions[0]) assert candle_results.successful() #ignore warning 1055, ICE82 from VC10 merge modules # ICE69: Mismatched component reference. Entry 'reg491FAFEB7F990D99C4A4D719B2A95253' of the Registry table belongs to component 'CyPhySoT.dll'. However, the formatted string in column 'Value' references file 'CyPhySoT.ico' which belongs to component 'CyPhySoT.ico' # ICE60: The file fil_5b64d789d9ad5473bc580ea7258a0fac is not a Font, and its version is not a companion file reference. It should have a language specified in the Language column. if source_wxs.startswith("META"): def download(): try: download_bundle_deps('META_bundle_x64.wxs') except Exception as e: pool_exceptions.append(sys.exc_info()) import threading download_thread = threading.Thread(target=download) download_thread.start() import datetime starttime = datetime.datetime.now() system([ 'light', '-sw1055', '-sice:ICE82', '-sice:ICE57', '-sice:ICE60', '-sice:ICE69', '-ext', 'WixNetFxExtension', '-ext', 'WixUIExtension', '-ext', 'WixUtilExtension', # '-cc', os.path.join(this_dir, 'cab_cache'), '-reusecab', # we were getting errors during installation relating to corrupted cab files => disable cab cache # udm.pyd depends on UdmDll_VC10 '-o', os.path.splitext(source_wxs)[0] + ".msi" ] + [get_wixobj(file) for file in sources] + [ 'UdmDll_VS10.wixlib', 'UdmDll_VC11_x64.wixlib', 'UdmDll_VC14.wixlib' ]) download_thread.join() if pool_exceptions: six.reraise(*pool_exceptions[0]) system( 'candle.exe META_bundle_x64.wxs -ext WixBalExtension -ext WixUtilExtension -ext WixDependencyExtension' .split() + ['-d' + d[0] + '=' + d[1] for d in defines]) system( 'candle.exe META_bundle_ba.wxi -ext WixBalExtension -ext WixUtilExtension -ext WixDependencyExtension' .split() + ['-d' + d[0] + '=' + d[1] for d in defines]) system(( 'light.exe -o META_bundle_x64' + ('_offline' if offline else '') + '.exe META_bundle_ba.wixobj META_bundle_x64.wixobj -ext WixBalExtension -ext WixUtilExtension -ext WixDependencyExtension -ext WixNetFxExtension' ).split()) print "elapsed time: %d seconds" % (datetime.datetime.now() - starttime).seconds else: msm_output = os.path.splitext(source_wxs)[0] + ".msm" system(['light', '-ext', 'WixUtilExtension', '-o', msm_output] + [get_wixobj(file) for file in sources])
def build_msi(): get_nuget_packages() add_wix_to_path() def get_wixobj(file): return os.path.splitext(file)[0] + ".wixobj" def adjacent_file(file): return os.path.join(os.path.dirname(__file__), file) gen_analysis_tool_wxi.main(r"..\analysis_tools", diskId='5') # gen_dir_from_vc: "explicit is better than implicit" # consider: generated files are left on disk after an svn switch, and get included in an installer that shouldn't have them gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\PCC\PCC",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\isis_meta\isis_meta",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\material_library\MaterialLibraryInterface",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\meta_nrmm\meta_nrmm",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\py_modelica\py_modelica",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\py_modelica_exporter\py_modelica_exporter",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\cad_library\cad_library",) gen_dir_wxi.gen_dir_from_vc(r"..\meta\DesignDataPackage\lib\python", "DesignDataPackage_python.wxi", "DesignDataPackage_python") gen_dir_wxi.main(r"CAD_Installs\Proe ISIS Extensions", "Proe_ISIS_Extensions_x64.wxi", "Proe_ISIS_Extensions_x64", diskId='4') # do not call gen_dir_from_vc, it would exclude CADCreoCreateAssembly.exe gen_dir_wxi.gen_dir_from_vc(r"..\WebGME",) gen_dir_wxi.gen_dir_from_vc(r"..\meta\CyPhyML\icons",) gen_dir_wxi.gen_dir_from_vc(r"..\models\MassSpringDamper",) gen_dir_wxi.gen_dir_from_vc(r"..\models\Validation",) gen_dir_wxi.gen_dir_from_vc(r"..\bin", diskId='3') gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\PCC\PCC",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\isis_meta\isis_meta",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\meta_nrmm\meta_nrmm",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\py_modelica\py_modelica",) gen_dir_wxi.gen_dir_from_vc(r"..\src\Python27Packages\py_modelica_exporter\py_modelica_exporter",) gen_dir_wxi.gen_dir_from_vc(r"..\src\CADAssembler\Python", id="CADPython") gen_dir_wxi.gen_dir_from_vc(r"..\meta\DesignDataPackage\lib\python", "DesignDataPackage_python.wxi", "DesignDataPackage_python") def get_svnversion(): p = subprocess.Popen("git rev-list HEAD --count".split(), stdout=subprocess.PIPE) out, err = p.communicate() return out.strip() or '5' #import subprocess #p = subprocess.Popen(['svnversion', '-n', adjacent_file('..')], stdout=subprocess.PIPE) #out, err = p.communicate() #if p.returncode: # raise subprocess.CalledProcessError(p.returncode, 'svnversion') #return out svnversion = get_svnversion() print "SVN version: " + str(get_svnversion()) sourcedir = adjacent_file('') def get_gitversion(): p = subprocess.Popen("git rev-parse --short HEAD".split(), stdout=subprocess.PIPE) out, err = p.communicate() #if p.returncode: # raise subprocess.CalledProcessError(p.returncode, 'svnversion') return out.strip() or 'unknown' gitversion = get_gitversion() import glob if len(sys.argv[1:]) > 0: source_wxs = sys.argv[1] else: source_wxs = 'META_x64.wxs' sources_all = glob.glob(sourcedir + '*.wxi') + glob.glob(sourcedir + source_wxs) sources = [] include_wxis = [] # For each each ComponentGroupRef in "source_wxs" and "analysis_tools.wxi", # add its corresponding file to "include_wxis" for wxs in glob.glob(sourcedir + source_wxs) + glob.glob(sourcedir + 'analysis_tools.wxi'): print 'Processing WXS: ' + wxs tree = ET.parse(wxs) root = tree.getroot() #print root all_nodes = root.findall('.//') for node in all_nodes: if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentGroupRef': include_wxis.append(node.attrib['Id'] + '.wxi') include_wxis.append(node.attrib['Id'] + '_x64.wxi') if 'Proe' in node.attrib['Id'] + '_x64.wxi': print node.attrib['Id'] + '_x64.wxi' if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentRef': include_wxis.append(node.attrib['Id'].rsplit( ".", 1 )[ 0 ] + '.wxi') include_wxis.append(node.attrib['Id'].rsplit( ".", 1 )[ 0 ] + '_x64.wxi') # For each file in include_wxis, check for ComponentGroupRef and ComponentRef. # Add any that you find index = 0 while index < len(include_wxis): wxi = include_wxis[index] index += 1 if not os.path.exists(wxi): continue tree = ET.parse(wxi) root = tree.getroot() all_nodes = root.findall('.//') for node in all_nodes: if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentGroupRef': include_wxis.append(node.attrib['Id'] + '.wxi') include_wxis.append(node.attrib['Id'] + '_x64.wxi') if node.tag == '{http://schemas.microsoft.com/wix/2006/wi}ComponentRef': include_wxis.append(node.attrib['Id'].rsplit( ".", 1 )[ 0 ] + '.wxi') include_wxis.append(node.attrib['Id'].rsplit( ".", 1 )[ 0 ] + '_x64.wxi') sources = [source for source in sources_all if (os.path.basename(source) in include_wxis)] sources.append(source_wxs) if len(sources) == 0: raise Exception("0 sources found in " + sourcedir) defines = [ ('InterpreterBin', '../src/bin') ] def get_mta_versions(mta_file): import uuid metaproject = win32com.client.Dispatch("MGA.MgaMetaProject") metaproject.Open('MGA=' + mta_file) try: return ("{" + str(uuid.UUID(bytes_le=metaproject.GUID)).upper() + "}", metaproject.Version) finally: metaproject.Close() cyphy_versions = get_mta_versions(adjacent_file('../generated/CyPhyML/models/CyPhyML.mta')) defines.append(('GUIDSTRCYPHYML', cyphy_versions[0])) defines.append(('VERSIONSTRCYPHYML', cyphy_versions[1])) version = '14.13.' if 'M' in svnversion: version = version + '1' else: # this will crash for switched or sparse checkouts version = version + str(int(svnversion)) print 'Installer version: ' + version defines.append(('VERSIONSTR', version)) defines.append(('SVNVERSION', svnversion)) defines.append(('GITVERSION', gitversion)) from multiprocessing.pool import ThreadPool pool = ThreadPool() pool_exceptions = [] def candle(source): try: arch = [ '-arch', ('x86' if source.find('x64') == -1 else 'x64') ] system(['candle', '-ext', 'WiXUtilExtension'] + ['-d' + d[0] + '=' + d[1] for d in defines ] + arch + [ '-out', get_wixobj(source), source] + ['-nologo']) except Exception as e: pool_exceptions.append(e) raise candle_results = pool.map_async(candle, sources, chunksize=1) pool.close() pool.join() if pool_exceptions: raise pool_exceptions[0] assert candle_results.successful() #ignore warning 1055, ICE82 from VC10 merge modules # ICE69: Mismatched component reference. Entry 'reg491FAFEB7F990D99C4A4D719B2A95253' of the Registry table belongs to component 'CyPhySoT.dll'. However, the formatted string in column 'Value' references file 'CyPhySoT.ico' which belongs to component 'CyPhySoT.ico' # ICE60: The file fil_5b64d789d9ad5473bc580ea7258a0fac is not a Font, and its version is not a companion file reference. It should have a language specified in the Language column. if source_wxs.startswith("META"): import datetime starttime = datetime.datetime.now() system(['light', '-sw1055', '-sice:ICE82', '-sice:ICE57', '-sice:ICE60', '-sice:ICE69', '-ext', 'WixNetFxExtension', '-ext', 'WixUIExtension', '-ext', 'WixUtilExtension', # '-cc', os.path.join(this_dir, 'cab_cache'), '-reusecab', # we were getting errors during installation relating to corrupted cab files => disable cab cache '-o', os.path.splitext(source_wxs)[0] + ".msi"] + [ get_wixobj(file) for file in sources ]) print "elapsed time: %d seconds" % (datetime.datetime.now() - starttime).seconds else: msm_output = os.path.splitext(source_wxs)[0] + ".msm" system(['light', '-ext', 'WixUtilExtension', '-o', msm_output] + [ get_wixobj(file) for file in sources ])