def bv_packaging(name, type_, folder, make_options=None, platform_target=None): """Package a component with no dependency. Parameters ---------- name : package name. type_ : type of package: run, doc, usrdoc, devdoc, test. folder : destination full path. platform_target : target platform to generate packages for """ args = [ os.path.join(Paths.BV_BIN, Paths.binary_name(Paths.BV_ENV_HOST, System.platform())), Paths.binary_name('python', System.platform()), os.path.join(Paths.BV_BIN, Paths.BV_PACKAGING), 'dir', '-o', folder, '--wrappers', '--no-deps', '--installer' ] if make_options is not None and len(make_options.strip()) > 0: args += ['--make-options', make_options] if platform_target is not None and len(platform_target.strip()) > 0: args += ['--platform-target', platform_target] args += ['+name=%s,type=%s' % (name, type_)] subprocess.check_call(args)
def __create_hacks(self): "Regroup all hacks for specific problems." qt_menu_nib = None if System.platform() == System.MacOSX \ and self.args.qt_menu_nib is None: # try to find qt_menu.nib in QtIFW import distutils.spawn binarycreator = distutils.spawn.find_executable( Paths.IFW_BINARYCREATOR) if binarycreator: real_path = os.path.realpath(binarycreator) path = os.path.dirname(os.path.dirname(real_path)) qt_menu_nib = os.path.join( path, "Uninstaller.app/Contents/Resources/qt_menu.nib") if not os.path.isdir(qt_menu_nib): qt_menu_nib = None # doesn't work else: qt_menu_nib = self.args.qt_menu_nib for installer in (self.args.installer, self.args.offline_installer): if installer is not None: if qt_menu_nib is not None: src = qt_menu_nib dst = "%s.app/Contents/Resources/qt_menu.nib" % installer try: shutil.copytree(src, dst) except Exception: # copying file attributes on the network may fail, # but copy is OK. pass if System.platform() == System.MacOSX \ and not self.args.repository_only: # create .dmg import distutils.spawn create_dmg = distutils.spawn.find_executable('create-dmg') if create_dmg: installer_path = '%s.dmg' % installer cmd = [ create_dmg, '--volname', 'BrainVISA-installer', '--volicon', '%s_tmp/config/icon.png' % self.args.repository, installer_path, '%s.app' % installer ] subprocess.check_call(cmd) else: raise RuntimeError('Impossible to find create-dmg ' 'executable. Please check it is ' 'available on the system.') # build the MD5 sum file import hashlib m = hashlib.md5() m.update(open(installer_path, 'rb').read()) mdsum = m.digest() mdsum_str = ''.join(['%02x' % ord(x) for x in mdsum]) open(installer_path + '.md5', 'w').write(mdsum_str)
def ifw_version(binary_creator_command=None, platform=None): """Try to guess IFW version. As the commands do not provide this info, all we can do for now is to try to find the "devtool" command, and guess it is version 2 if it is found, and 1 otherwise. """ if not platform: platform = System.platform() if not binary_creator_command: bc = distutils.spawn.find_executable( Paths.binary_name(Paths.IFW_BINARYCREATOR, platform)) else: bc = binary_creator_command if not bc: return [] # undefined real_bc = os.path.realpath(bc) path = os.path.dirname(real_bc) if os.path.exists( os.path.join(path, Paths.binary_name(Paths.IFW_DEVTOOL, platform))): return [ 2, ] return [ 1, ]
def repogen(path_repository_in, path_repository_out, components=None, update=False, exclude=None): # pylint: disable=R0913 """The repogen tool generates an online IFW repositoriy. Parameters ---------- path_repository_in : full path of temporary repository. path_repository_out : full path of IFW repository. components : additional components (default None). update : True if the existing IFW repository must be updated. exclude : list of excluded package's names (default None). """ param_components = [components.join(',')] if exclude else [] param_update = ['--update'] if update else [] param_exclude = ['--exclude', exclude.join(',')] if exclude else [] # param_updateurl = '-u %s' % updateurl if updateurl else '' param_packages = ["-p", "%s/packages" % path_repository_in] cmd = [Paths.binary_name(Paths.IFW_REPOGEN, System.platform())] \ + param_packages + param_update + param_exclude \ + param_components + [path_repository_out] # param_updateurl, print(' '.join(cmd)) subprocess.check_call(cmd)
def translate_path(path, platform_target, translation_type=PathTranslationType.HOST_TO_TARGET): """Translate path between platform host and target.""" platform_host = System.platform() platform_host_family = System.platform_family(platform_host) platform_target_family = System.platform_family(platform_target) # print('==== translate_path') if platform_host != platform_target.upper(): if platform_host_family == System.Family.Linux \ and platform_target_family == System.Family.Win: return translate_path_wine(path, translation_type) else: raise RuntimeError('No known path translation method between ' '%s (%s family) and %s (%s family) systems' % (platform_host, platform_host_family, platform_target, platform_target_family)) else: return path
def exception_info_by_name(self, name, param): "Return the exception value from name and param." exceptions = self.root.find('EXCEPTIONS') if exceptions is not None: for exception in exceptions: if exception.tag == 'INFO' and \ exception.attrib.get('NAME') == name and \ exception.attrib.get('PARAM') == param: platform = exception.attrib.get('PLATFORM') if platform: if platform != System.platform(): return None return exception.attrib.get('VALUE') return None
def __create_installer(self): "Create the binary installer(s)." if not self.args.repository_only: if self.args.installer is not None: if self.args.offline_installer is None: online_only = self.args.online_only offline_only = self.args.offline_only else: online_only = True offline_only = False logging.getLogger().info(MESSAGE_BVI_INSTALLER) binarycreator( self.args.installer, "%s_tmp" % self.args.repository, online_only=online_only, offline_only=offline_only, platform_target=self.args.platform_target if self.args.platform_target else System.platform().lower(), command=self.args.binary_creator_command if self.args.binary_creator_command else None) if self.args.offline_installer is not None: online_only = False offline_only = self.args.offline_only logging.getLogger().info(MESSAGE_BVI_OFFLINE_INSTALLER) binarycreator( self.args.offline_installer, "%s_tmp" % self.args.repository, additional_repositories=[ os.path.join("%s_tmp", "packages") % r for r in self.args.additional_repositories ], online_only=online_only, offline_only=offline_only, platform_target=self.args.platform_target if self.args.platform_target else System.platform().lower(), command=self.args.binary_creator_command if self.args.binary_creator_command else None)
def is_package_excluded(self, name): "Return False if the package must be excluded." exceptions = self.root.find('EXCEPTIONS') excluded = self.data_packages if exceptions is not None: for exception in exceptions: if (exception.tag == 'PACKAGE' and exception.attrib.get('NAME') == name): platform = exception.attrib.get('PLATFORM') if platform and platform != System.platform(): continue etype = exception.attrib.get('TYPE') if etype == 'DATA_PACKAGE': excluded = not self.data_packages elif etype == 'ALL': return True return excluded
def init_from_configuration(self, element): "Initialize from an XML element from XML configuration file." self.Url = element.text.strip() self.Url = self.Url.replace('@release@', self.Release) self.Url = self.Url.replace('@platform@', self.PlatformName) att_platform = element.attrib.get('PLATFORM') self.Enabled = '1' if att_platform is None \ or System.platform().lower() == att_platform.lower() else '0' # att_private = element.attrib.get('PRIVATE') # if att_private is not None and att_private in ('1', 'True', 'true'): # self.Enabled = '0' if self.Url.startswith("file://") and not self.private: self.Enabled = '0' self.Username = element.attrib.get('USERNAME') self.Password = element.attrib.get('PASSWORD') self.DisplayName = element.attrib.get('DISPLAYNAME') return self
def archivegen(folder): """The archivegen tool compresses the files in folder as a 7zip archive. The archive will have the same name what the folder with the 7z extension. Parameter --------- folder: folder with data which must be compressed. """ command = Paths.binary_name(Paths.IFW_ARCHIVEGEN, System.platform()) archive = '%s.7z' % folder args = [command] + Paths.ARCHIVEGEN_OPTIONS + [archive, '%s' % folder] print(' '.join(args)) if os.path.exists(archive): os.unlink(archive) process = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=folder) result = process.wait() logging.getLogger().info(result) if result < 0: raise BVIException(BVIException.ARCHIVEGEN_FAILED, folder)
def __init__(self, argv): "Parse the command line arguments." parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=MESSAGE_HELP_HEADER, epilog=MESSAGE_HELP_EPILOG) parser.add_argument('-p', '--projects', type=valid_projects, nargs='+', metavar='project', help='Projects to include in the installer and ' 'the repository') parser.add_argument('-n', '--names', type=valid_names, nargs='+', metavar='name', help='Package names to include in the installer ' 'and the repository') parser.add_argument('-t', '--types', nargs='+', choices=['run', 'dev', 'usrdoc', 'devdoc', 'test'], default=['run', 'dev', 'usrdoc', 'devdoc', 'test'], metavar='types', help='Package\'s types (default: "run", "dev", ' '"usrdoc", "devdoc" and "test")') parser.add_argument('--online-only', action='store_true', help='Create only an online installer') parser.add_argument('--offline-only', action='store_true', help='Create only an offline installer') parser.add_argument('--repository-only', action='store_true', help='Create only the repository for the online ' 'installer') parser.add_argument('--compress', action='store_true', help='The packages data in the temporary ' 'repository will be compressed [experimental].') parser.add_argument( '-i', '--installer', default=None, metavar='file', help= 'Installer name (optional only if --repository-only is specified). Note: if an additional offline installer (-j option) is specified, -i will be an online-only installer. Otherwise it will follow the options --online-only and --offline-only if they are specified, with the same meaning as in the binarycreator tool.' ) parser.add_argument( '-j', '--offline-installer', default=None, metavar='file', help= 'Offline installer name (optional). The offline installer will contain all packages, ignoring --online-only option, but still following --offline-only if specified. Using both -i and -j will generate two installer binaries, one online and one offline.' ) parser.add_argument('-r', '--repository', default=None, metavar='dir', required=True, help='Repository name.') parser.add_argument( '-f', '--additional-repositories', default=[], nargs='+', metavar='additional_dir', help= 'Additional existing repositories (optional). This is particularly usefull for offline installer that need to contain all installation packages. This packages may exists in separated repositories.' ) parser.add_argument('-c', '--config', type=valid_config, default=None, metavar='file', help='Additional configuration XML file') parser.add_argument('--no-main-config', action='store_true', help='don\'t read the main BrainVisa config file. ' 'Must be used with the -c option') parser.add_argument( '--qt_menu_nib', default=None, help= 'For Mac OS X 10.5: copy the specified qt_menu.nib folder in the \ installer OSX App package. Use this option if the OS X installer \ did not find the qt_menu.nib folder.') parser.add_argument('--release', type=valid_release, default=None, help='force repository release version. default: ' 'use BrainVISA release version from the current ' 'build.') parser.add_argument( '--i2bm', action='store_true', help= 'Include I2BM private components - by default such private components are excluded from the package.' ) parser.add_argument('--data', action='store_true', help='Package only data packages (which are ' 'excluded from normal packaging).') parser.add_argument('-v', '--version', action='version', version='%(prog)s [' + __status__ + '] - ' + __version__, help='Show the version number.') parser.add_argument('--no-thirdparty', action='store_true', help='Do not package thirdparty libraries, and ' 'ignore them in dependencies.') parser.add_argument( '--no-dependencies', action='store_true', help= 'Do not package dependencies: take only explicitely named packages/projects. Their dependencies will still be marked so they must either already exist in the repository, either exist in another repository.' ) parser.add_argument('--platform-target', dest='platform_target', default=None, help='target platform to use for cross ' 'compilation (default: %s)' % System.platform().lower()) parser.add_argument('--platform_name', default=None, help='force platform name in packages repository ' 'URL (default: %s)' % System.platform().lower()) parser.add_argument('--make-options', dest='make_options', default=None, help='make options to use during components ' 'packaging') parser.add_argument('--binary-creator-cmd', dest='binary_creator_command', default=None, help='Path to the binary creator command to use ' 'to generate the installer.') parser.add_argument('--archivegen-cmd', dest='archivegen_cmd', default=None, help='Path to the archivegen command to use to ' 'generate 7z archives. Default: look (in that ' 'order) for: 7z, 7za, archivegen.') parser.add_argument('--archivegen-opts', dest='archivegen_opts', default='', help='archivegen command options. Default: none ' 'for archivegen, "a" for 7z/7za. Options are ' 'split by space character.') parser.add_argument('--skip-repos', dest='skip_repos', action='store_true', help='Skip initial (temp) repository creation. ' 'Assumes it has already been done.') parser.add_argument('--skip-repogen', dest='skip_repogen', action='store_true', help='Skip repogen (final repository creation + ' 'compression). Assumes it has already been done.') parser.add_argument('--skip-existing', dest='skip_existing', action='store_true', help='Don\'t rebuild components which already ' 'have a directory in the temporary repository ' 'directory.') self.__configure_logging() args = parser.parse_args(argv[1:]) if args.online_only + args.offline_only + args.repository_only > 1: logging.getLogger().error( "[ BVI ] Error: --online-only, --offline-only and " "--repository-only are incompatible.") exit(1) if args.qt_menu_nib is not None: if args.installer is None and args.offline_installer is None: logging.getLogger().error( "[ BVI ] Error: --installer and/or --offline_installer " "must be specified if --qt_menu_nib is used.") exit(1) if System.platform() != System.MacOSX: logging.getLogger().error( "[ BVI ] Error: --qt_menu_nib is only for Mac OS X.") exit(1) self.args = args self.logging_level = logging.DEBUG if self.args.no_main_config: kwargs = {'filename': self.args.config} else: kwargs = {'alt_filename': self.args.config} if self.args.release is not None: kwargs['release'] = self.args.release if self.args.no_thirdparty: kwargs['with_thirdparty'] = False if self.args.no_dependencies: kwargs['with_dependencies'] = False if self.args.platform_target: kwargs['platform_target'] = self.args.platform_target if not self.args.platform_name: # Defaultly use target platform as platform name self.args.platform_name = self.args.platform_target if self.args.platform_name: kwargs['platform_name'] = self.args.platform_name kwargs['make_options'] = self.args.make_options kwargs['binary_creator_command'] = self.args.binary_creator_command kwargs['archivegen_cmd'] = self.args.archivegen_cmd kwargs['archivegen_opts'] = self.args.archivegen_opts.split(' ') if kwargs['archivegen_opts'] == ['']: kwargs['archivegen_opts'] = [] kwargs['skip_repos'] = self.args.skip_repos kwargs['skip_repogen'] = self.args.skip_repogen kwargs['skip_existing'] = self.args.skip_existing kwargs['data_packages'] = self.args.data kwargs['private_repos'] = self.args.i2bm self.config = Configuration(**kwargs) self.components = self.__group_components()
def __init__(self, filename=Paths.BVI_CONFIGURATION, alt_filename=None, release=None, with_dependencies=True, with_thirdparty=True, platform_target=None, platform_name=None, skip_repos=False, skip_repogen=False, skip_existing=False, data_packages=False, private_repos=False, make_options=None, binary_creator_command=None, archivegen_cmd=None, archivegen_opts=[]): "filename is the default configuration file in share, \ alt_filename is an optional configuration file \ to override the default configuration." self.tree = None self.root = None self.Name = None self.Version = None self.Title = None self.Publisher = None self.Producturl = None self.Targetdir = None self.Admintargetdir = None self.Icon = None self.Logo = None self.Watermark = None self.MaintenanceToolName = None self.Allownonasciicharacters = None self.Allowspaceinpath = None self.Repositories = list() self.Licenses = list() self.Categories = list() self.Release = release if binary_creator_command: self.IFWVersion = ifw_version(binary_creator_command, platform_target) else: self.IFWVersion = None if platform_name is None: platform_name = System.platform().lower() self.PlatformName = platform_name if platform_target is None: platform_target = System.platform().lower() self.platform_target = platform_target self.make_options = make_options self.binary_creator_command = binary_creator_command self.archivegen_opts = archivegen_opts self.with_dependencies = with_dependencies self.with_thirdparty = with_thirdparty self.skip_repos = skip_repos self.skip_repogen = skip_repogen self.skip_existing = skip_existing self.data_packages = data_packages self.private_repos = private_repos self.read(filename) if alt_filename is not None: self.read(alt_filename) if archivegen_cmd is None: archivegen_opts = [] archivegen_cmd = distutils.spawn.find_executable('7z') if archivegen_cmd is not None: archivegen_opts = ['a'] else: archivegen_cmd = distutils.spawn.find_executable('7za') if archivegen_cmd is not None: archivegen_opts = ['a'] else: archivegen_cmd \ = distutils.spawn.find_executable('archivegen') # print('archivegen_cmd:', archivegen_cmd) # print('archivegen_opts:', archivegen_opts) self.archivegen_cmd = archivegen_cmd self.archivegen_opts = archivegen_opts if self.archivegen_cmd is not None: Paths.IFW_ARCHIVEGEN = self.archivegen_cmd Paths.ARCHIVEGEN_OPTIONS = self.archivegen_opts
def binarycreator(installer_path, repository_path, additional_repositories=[], online_only=False, offline_only=False, exclude=None, include=None, platform_target=System.platform(), command=None): """The binarycreator tool creates an IFW installer. Parameters ---------- installer_path : full path of installer binary. repository_path : full path of temporary repository. additional_repositories : additional repositories to find packages in. online_only : True if the installer is only online (default False). offline_only : True if the installer is only offline (default False). exclude : list of excluded package's names (default None). include : list of included package's names (default None). platform_target : target platform to generate installer binary on (default is the host platform) command : binarycreator command to use (default:) """ param_online_only = ['--online-only'] if online_only else [] param_offline_only = ['--offline-only'] if offline_only else [] param_exclude = ['--exclude', exclude.join(',')] if exclude else [] param_include = ['--include', include.join('')] if include else [] param_config = [ '-c', translate_path('%s/config/config.xml' % repository_path, platform_target) ] param_packages = [ '-p', translate_path('%s/packages' % repository_path, platform_target) ] for r in additional_repositories: param_packages += ['-p', translate_path(r, platform_target)] path = os.path.dirname(installer_path) if not os.path.exists(path): os.makedirs(path) # Starts binary creator through target bv_env cmd = [command if command else Paths.binary_name(Paths.IFW_BINARYCREATOR, platform_target)] \ + param_online_only + param_offline_only + param_exclude \ + param_include + param_config + param_packages \ + [translate_path(installer_path, platform_target)] print(' '.join(cmd)) subprocess.check_call(cmd) if System.platform() == System.MacOSX: return # don't do the .md5 now: we must build the .dmg first. # build the MD5 sum file m = hashlib.md5() m.update(open(installer_path, 'rb').read()) mdsum = m.digest() if sys.version_info[0] >= 3: mdsum_str = ''.join(['%02x' % x for x in mdsum]) else: mdsum_str = ''.join(['%02x' % ord(x) for x in mdsum]) open(installer_path + '.md5', 'w').write(mdsum_str)