зеркало из https://github.com/mozilla/pjs.git
369 строки
13 KiB
Python
369 строки
13 KiB
Python
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is Mozilla build system.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# Mozilla Foundation.
|
|
# Portions created by the Initial Developer are Copyright (C) 2007
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# John Wolfe <wolfe@lobo.us>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
# ***** END LICENSE BLOCK *****
|
|
################################################################
|
|
#
|
|
# make-wince-cab.py --- Given a directory, walk it and make an
|
|
# installer based upon the contents of that directory
|
|
#
|
|
# Usage: python make-wince-inf.py CABWIZ_PATH SOURCE_DIR PROGRAM_NAME CAB_FINAL_NAME
|
|
#
|
|
# Walk through the relative directory SOURCE_DIR, parsing filenames
|
|
# Checks for duplicate filenames and renames where needed
|
|
# Then builds a WinMobile INF file based upon results
|
|
#
|
|
# Each directory in SOURCE_DIR may have a file named
|
|
# 'install-exceptions', which lists files in SOURCE_DIR that
|
|
# need not be placed into an installation CAB file. The
|
|
# 'install-exceptions' file itself is always ignored.
|
|
#
|
|
# Blank lines and '#' comments in the 'install-exceptions' file are
|
|
# ignored.
|
|
#
|
|
# EXAMPLE OF COMMAND LINE:
|
|
# python make_wince_inf.py /c/Program\ Files/Microsoft\ Visual\ Studio\ 9.0/SmartDevices/SDK/SDKTools/cabwiz.exe dist/fennec Fennec fennec-0.11.en-US.wince-arm.cab
|
|
#
|
|
# ARGS:
|
|
# cabiz_path - Path to CABWIZ.EXE executable inside Visual Studio
|
|
#
|
|
# source_dir - sub-directory which contains the target program
|
|
# NOTE: It is assumed that the application name is SOURCE_DIR.exe
|
|
# EXAMPLE: source_dir=fennec, there should be a fennec/fennec.exe application
|
|
#
|
|
# program_name - Name of the program to place inside the INF file
|
|
#
|
|
# cab_final_name - actual final name for the produced CAB file
|
|
#
|
|
# NOTE: In our example, "fennec" is the directory [source_name]
|
|
# "fennec.exe" is the application [$(source_name).exe], and
|
|
# "Fennec" is the shortcut name [program_name]
|
|
################################################################
|
|
|
|
import sys
|
|
import os
|
|
from os.path import join
|
|
from subprocess import call, STDOUT
|
|
import fnmatch
|
|
import string
|
|
import shutil
|
|
|
|
|
|
class FileEntry:
|
|
def __init__(self, dirpath, dircount, filename, filecount, actual_filename):
|
|
self.dirpath = dirpath
|
|
self.dircount = dircount
|
|
self.filename = filename
|
|
self.filecount = filecount
|
|
self.actual_filename = actual_filename
|
|
|
|
|
|
class DirEntry:
|
|
def __init__(self, dirpath, dircount, filecount):
|
|
self.dirpath = dirpath
|
|
self.dircount = dircount
|
|
self.filecount = filecount
|
|
|
|
# Ignore detritus left lying around by editing tools.
|
|
ignored_patterns = ['*~', '.#*', '#*#', '*.orig', '*.rej']
|
|
|
|
file_entry_count = 0
|
|
file_entries = []
|
|
|
|
fcount = 0
|
|
fnames = {}
|
|
|
|
directories = {}
|
|
files_in_directory = []
|
|
|
|
# Return the contents of FILENAME, a 'install-exceptions' file, as
|
|
# a dictionary whose keys are exactly the list of filenames, along
|
|
# with the basename of FILENAME itself. If FILENAME does not exist,
|
|
# return the empty dictionary.
|
|
def read_exceptions(filename):
|
|
exceptions = set()
|
|
if os.path.exists(filename):
|
|
f = file(filename)
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and line[0] != '#':
|
|
exceptions.add(line)
|
|
exceptions.add( os.path.basename(filename) )
|
|
f.close()
|
|
return exceptions
|
|
|
|
|
|
# Sorts two items based first upon directory count (index of
|
|
# directory in list of directories), then upon filename. A way of
|
|
# getting a list sorted into directories, and then alphabetically by
|
|
# filename within each directory.
|
|
def sort_dircount_then_filename(item1, item2):
|
|
# First Compare dirpaths
|
|
if item1.dircount != item2.dircount:
|
|
return cmp(item1.dircount, item2.dircount)
|
|
|
|
# Next compare filenames
|
|
next_result = cmp(item1.filename, item2.filename)
|
|
if next_result != 0:
|
|
return next_result
|
|
|
|
# Lastly, compare filecounts
|
|
return cmp(item1.filecount, item2.filecount)
|
|
|
|
|
|
def handle_duplicate_filename(dirpath, filename, filecount):
|
|
if filecount == 1:
|
|
return filename
|
|
file_parts = os.path.splitext(filename)
|
|
new_filename = "%s_%d%s" % (file_parts[0], filecount, file_parts[1])
|
|
old_relative_filename = join(dirpath, filename)
|
|
new_relative_filename = join(dirpath, new_filename)
|
|
shutil.copyfile(old_relative_filename, new_relative_filename)
|
|
return new_filename
|
|
|
|
def add_new_entry(dirpath, dircount, filename, filecount):
|
|
global file_entries
|
|
global file_entry_count
|
|
actual_filename = handle_duplicate_filename(dirpath, filename, filecount)
|
|
file_entries.append( FileEntry(dirpath, dircount, filename, filecount, actual_filename) )
|
|
file_entry_count += 1
|
|
|
|
|
|
def walk_tree(start_dir, ignore):
|
|
global fcount
|
|
global fnames
|
|
global directories
|
|
global files_in_directory
|
|
|
|
dircount = 0;
|
|
files_in_directory = [0]
|
|
|
|
for (dirpath, dirnames, filenames) in os.walk(start_dir):
|
|
exceptions = read_exceptions(join(dirpath, 'install-exceptions'))
|
|
|
|
dircount += 1
|
|
directories[dirpath] = DirEntry(dirpath, dircount, len(filenames))
|
|
|
|
if len(filenames) < 1:
|
|
print "WARNING: No files in directory [%s]" % dirpath
|
|
|
|
for filename in filenames:
|
|
if len(exceptions) > 0 and filename in exceptions:
|
|
continue
|
|
if any(fnmatch.fnmatch(filename, p) for p in ignore):
|
|
continue
|
|
filecount = 1
|
|
if filename in fnames:
|
|
filecount = fnames[filename] + 1
|
|
fnames[filename] = filecount
|
|
|
|
add_new_entry(dirpath, dircount, filename, filecount)
|
|
|
|
|
|
def output_inf_file_header(f, program_name):
|
|
f.write("""; Duplicated Filenames ==> One of the two files
|
|
; needs renaming before packaging up the CAB
|
|
;
|
|
; Technique: Rename the second directory's file to XXXX_1 prior to starting the CABWIZ,
|
|
; then take care of the name in the File.xxxxx section
|
|
|
|
[Version]
|
|
Signature = "$Windows NT$" ; required as-is
|
|
Provider = "Mozilla" ; maximum of 30 characters, full app name will be \"<Provider> <AppName>\"
|
|
CESignature = "$Windows CE$" ; required as-is
|
|
|
|
[CEStrings]
|
|
AppName = "%s" ; maximum of 40 characters, full app name will be \"<Provider> <AppName>\"\n""" % program_name)
|
|
|
|
f.write("InstallDir = %CE1%\\%AppName% ; Program Files\Fennec\n\n")
|
|
|
|
|
|
def output_sourcedisksnames_section(f, dirs):
|
|
f.write("[SourceDisksNames] ; directory that holds the application's files\n")
|
|
for dir in dirs:
|
|
f.write("""%d = , "%s",,%s\n""" % (directories[dir].dircount, dir, dir))
|
|
f.write(" \n")
|
|
|
|
|
|
def output_sourcedisksfiles_section(f):
|
|
f.write("[SourceDisksFiles] ; list of files to be included in .cab\n")
|
|
for entry in sorted(file_entries, sort_dircount_then_filename):
|
|
f.write("%s=%d\n" % (entry.actual_filename, entry.dircount))
|
|
f.write("\n")
|
|
|
|
|
|
def output_defaultinstall_section(f, dirs):
|
|
copyfileslist = ""
|
|
copyfilesrawlist=[]
|
|
for dir in dirs:
|
|
if directories[dir].filecount < 1:
|
|
continue
|
|
copyfilesrawlist.append( "Files.%s" % '.'.join( dir.split('\\') ) )
|
|
prefix = ", "
|
|
copyfileslist = ','.join(copyfilesrawlist)
|
|
|
|
f.write("""[DefaultInstall] ; operations to be completed during install
|
|
CopyFiles = %s
|
|
AddReg = RegData
|
|
CEShortcuts = Shortcuts
|
|
\n""" % copyfileslist)
|
|
|
|
|
|
def output_destinationdirs_section(f, dirs):
|
|
f.write("[DestinationDirs] ; default destination directories for each operation section\n")
|
|
for dir in dirs:
|
|
dir_split = dir.split('\\')
|
|
mod_dir_string = '.'.join(dir_split)
|
|
if len(dir_split) > 1:
|
|
dir_minus_top_level = '\\'.join(dir_split[1:])
|
|
else:
|
|
dir_minus_top_level = ""
|
|
if dir_minus_top_level:
|
|
dir_minus_top_level = "\\%s" % dir_minus_top_level
|
|
if directories[dir].filecount < 1:
|
|
f.write(";Files.%s = 0, %%InstallDir%%%s ; NO FILES IN THIS DIRECTORY\n" % (mod_dir_string, dir_minus_top_level))
|
|
else:
|
|
f.write("Files.%s = 0, %%InstallDir%%%s\n" % (mod_dir_string, dir_minus_top_level))
|
|
f.write("Shortcuts = 0, %CE11% ; \Windows\Start Menu\Programs\n\n")
|
|
|
|
|
|
def output_directory_sections(f, dirs):
|
|
for dir in dirs:
|
|
if directories[dir].filecount < 1:
|
|
f.write(";[Files.%s]\n;===NO FILES===\n" % '.'.join( dir.split('\\') ))
|
|
else:
|
|
f.write("[Files.%s]\n" % '.'.join( dir.split('\\') ))
|
|
for entry in file_entries:
|
|
if entry.dirpath == dir:
|
|
f.write("\"%s\",%s\n" % (entry.filename, entry.actual_filename))
|
|
f.write("\n")
|
|
|
|
|
|
def output_registry_section(f):
|
|
f.write("""[RegData] ; registry key list
|
|
HKCU,Software\%AppName%,MajorVersion,0x00010001,1
|
|
HKCU,Software\%AppName%,MinorVersion,0x00010001,0
|
|
\n""")
|
|
|
|
|
|
def output_shortcuts_section(f, app_name):
|
|
f.write("[Shortcuts] ; Shortcut created in destination dir, %CE14%\n")
|
|
f.write("%%AppName%%,0,%s\n" % app_name)
|
|
|
|
|
|
def output_inf_file(program_name, app_name):
|
|
global files_in_directory
|
|
inf_name = "%s.inf" % program_name
|
|
f = open(inf_name, 'w')
|
|
output_inf_file_header(f, program_name)
|
|
dirs = sorted(directories)
|
|
output_sourcedisksnames_section(f, dirs)
|
|
output_sourcedisksfiles_section(f)
|
|
output_defaultinstall_section(f, dirs)
|
|
output_destinationdirs_section(f, dirs)
|
|
output_directory_sections(f, dirs)
|
|
output_registry_section(f)
|
|
output_shortcuts_section(f, app_name)
|
|
f.close()
|
|
|
|
|
|
|
|
def make_cab_file(cabwiz_path, program_name, cab_final_name):
|
|
make_cab_command = "\"%s\" %s.inf /compress" % (cabwiz_path, program_name)
|
|
print "INFORMATION: Executing command to make %s CAB file (only works on BASH)" % program_name
|
|
print " [%s]" % make_cab_command
|
|
sys.stdout.flush()
|
|
|
|
success = call([cabwiz_path, "%s.inf" % program_name, "/compress"],
|
|
stdout=open("NUL:","w"), stderr=STDOUT)
|
|
|
|
if not os.path.isfile("%s.CAB" % program_name):
|
|
print """***************************************************************************
|
|
ERROR: CAB FILE NOT CREATED.
|
|
You can try running the command by hand:
|
|
%s" % make_cab_comman
|
|
----
|
|
NOTE: If you see an error like this:
|
|
Error: File XXXXXXXXXX.inf contains DirIDs, which are not supported
|
|
--
|
|
this may mean that your PYTHON is outputting Windows files WITHOUT CR-LF
|
|
line endings. Please verify that your INF file has CR-LF line endings.
|
|
***************************************************************************"""
|
|
return
|
|
|
|
print "INFORMATION: Executing command to move %s.CAB to %s" % (program_name, cab_final_name)
|
|
sys.stdout.flush()
|
|
shutil.move("%s.CAB" % program_name, cab_final_name)
|
|
|
|
|
|
def purge_copied_files():
|
|
for entry in file_entries:
|
|
if entry.filename != entry.actual_filename:
|
|
new_relative_filename = join(entry.dirpath, entry.actual_filename)
|
|
os.remove(new_relative_filename)
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) != 5:
|
|
print >> sys.stderr, "Usage: %s CABWIZ_PATH SOURCE_DIR PROGRAM_NAME CAB_FINAL_NAME" % sys.argv[0]
|
|
print >> sys.stderr, "Example: %s /c/Program\ Files/Microsoft\ Visual\ Studio\ 9.0/ fennec Fennec fennec-0.11.en-US.wince-arm.cab" % sys.argv[0]
|
|
sys.exit(1)
|
|
|
|
cabwiz_path = sys.argv[1]
|
|
source_dir = sys.argv[2]
|
|
program_name = sys.argv[3]
|
|
app_name = "%s.exe" % source_dir
|
|
cab_final_name = sys.argv[4]
|
|
|
|
if not os.path.isfile(cabwiz_path):
|
|
print """***************************************************************************
|
|
ERROR: CABWIZ_PATH is not a valid file!
|
|
Perhaps your VSINSTALLDIR is not properly set up?
|
|
EXITING...
|
|
***************************************************************************"""
|
|
sys.exit(2)
|
|
|
|
walk_tree(source_dir, ignored_patterns)
|
|
sys.stdout.flush()
|
|
output_inf_file(program_name, app_name)
|
|
sys.stdout.flush()
|
|
make_cab_file(cabwiz_path, program_name, cab_final_name)
|
|
purge_copied_files()
|
|
|
|
|
|
# run main if run directly
|
|
if __name__ == "__main__":
|
|
main()
|