X-Git-Url: https://main.carlh.net/gitweb/?p=cdist.git;a=blobdiff_plain;f=cdist;h=06a9cb30af71ba66ea0f7a34f739532a6a640df4;hp=da87f8473986e64d866b2ae7fc8dbe9ce3a635f1;hb=HEAD;hpb=dd494941b8214c08d2d7f019a948c569583f3fa5 diff --git a/cdist b/cdist index da87f84..fcc34b5 100755 --- a/cdist +++ b/cdist @@ -79,6 +79,7 @@ class Globals: quiet = False command = None dry_run = False + use_git_reference = True trees = Trees() globals = Globals() @@ -88,7 +89,7 @@ globals = Globals() # Configuration # -class Option(object): +class Option: def __init__(self, key, default=None): self.key = key self.value = default @@ -97,7 +98,7 @@ class Option(object): if key == self.key: self.value = value -class BoolOption(object): +class BoolOption: def __init__(self, key): self.key = key self.value = False @@ -121,12 +122,14 @@ class Config: Option('osx_keychain_password'), Option('apple_id'), Option('apple_password'), + Option('apple_team_id'), BoolOption('docker_sudo'), BoolOption('docker_no_user'), Option('docker_hub_repository'), Option('flatpak_state_dir'), Option('parallel', multiprocessing.cpu_count()), - Option('temp', '/var/tmp')] + Option('temp', '/var/tmp'), + Option('osx_notarytool', ['xcrun', 'notarytool'])] config_dir = '%s/.config' % os.path.expanduser('~') if not os.path.exists(config_dir): @@ -140,22 +143,23 @@ class Config: print('Template config file written to %s; please edit and try again.' % config_file, file=sys.stderr) sys.exit(1) - try: - f = open('%s/.config/cdist' % os.path.expanduser('~'), 'r') - while True: - l = f.readline() - if l == '': - break - - if len(l) > 0 and l[0] == '#': - continue - - s = l.strip().split() - if len(s) == 2: - for k in self.options: - k.offer(s[0], s[1]) - except: - raise + f = open('%s/.config/cdist' % os.path.expanduser('~'), 'r') + while True: + l = f.readline() + if l == '': + break + + if len(l) > 0 and l[0] == '#': + continue + + s = l.strip().split() + if len(s) == 2: + for k in self.options: + k.offer(s[0], s[1]) + + if not isinstance(self.get('osx_notarytool'), list): + self.set('osx_notarytool', [self.get('osx_notarytool')]) + def has(self, k): for o in self.options: @@ -323,67 +327,12 @@ class TreeDirectory: def __exit__(self, type, value, traceback): os.chdir(self.cwd) -# -# Version -# - -class Version: - def __init__(self, s): - self.devel = False - - if s.startswith("'"): - s = s[1:] - if s.endswith("'"): - s = s[0:-1] - - if s.endswith('devel'): - s = s[0:-5] - self.devel = True - - if s.endswith('pre'): - s = s[0:-3] - - p = s.split('.') - self.major = int(p[0]) - self.minor = int(p[1]) - if len(p) == 3: - self.micro = int(p[2]) - else: - self.micro = 0 - - @classmethod - def from_git_tag(cls, tag): - bits = tag.split('-') - c = cls(bits[0]) - if len(bits) > 1 and int(bits[1]) > 0: - c.devel = True - return c - - def bump_minor(self): - self.minor += 1 - self.micro = 0 - - def bump_micro(self): - self.micro += 1 - - def to_devel(self): - self.devel = True - - def to_release(self): - self.devel = False - - def __str__(self): - s = '%d.%d.%d' % (self.major, self.minor, self.micro) - if self.devel: - s += 'devel' - - return s # # Targets # -class Target(object): +class Target: """ Class representing the target that we are building for. This is exposed to cscripts, though not all of it is guaranteed 'API'. cscripts may expect: @@ -400,7 +349,7 @@ class Target(object): """ - def __init__(self, platform, directory=None): + def __init__(self, platform, directory=None, dependencies_only=False): """ platform -- platform string (e.g. 'windows', 'linux', 'osx') directory -- directory to work in; if None we will use a temporary directory @@ -416,8 +365,14 @@ class Target(object): # True to build our dependencies ourselves; False if this is taken care # of in some other way self.build_dependencies = True + self.dependencies_only = dependencies_only if directory is None: + try: + os.makedirs(config.get('temp')) + except OSError as e: + if e.errno != 17: + raise e self.directory = tempfile.mkdtemp('', 'tmp', config.get('temp')) self.rmdir = True self.set('CCACHE_BASEDIR', os.path.realpath(self.directory)) @@ -434,9 +389,10 @@ class Target(object): """ Call package() in the cscript and return what it returns, except that anything not in a list will be put into one. + options: from command line """ if len(inspect.getfullargspec(tree.cscript['package']).args) == 3: - packages = tree.call('package', tree.version, options) + packages = tree.call('package', tree.version, tree.add_defaults(options)) else: log_normal("Deprecated cscript package() method with no options parameter") packages = tree.call('package', tree.version) @@ -448,8 +404,10 @@ class Target(object): copyfile(p, os.path.join(output_dir, os.path.basename(devel_to_git(tree.commit, p)))) def package(self, project, checkout, output_dir, options, notarize): + """ + options: from command line + """ tree = self.build(project, checkout, options, for_package=True) - tree.add_defaults(options) p = self._cscript_package(tree, options) self._copy_packages(tree, p, output_dir) @@ -457,17 +415,17 @@ class Target(object): tree = globals.trees.get(project, checkout, self) if self.build_dependencies: tree.build_dependencies(options) - tree.build(options, for_package=for_package) + if not self.dependencies_only: + tree.build(options, for_package=for_package) return tree def test(self, project, checkout, target, test, options): """test is the test case to run, or None""" tree = globals.trees.get(project, checkout, target) - tree.add_defaults(options) with TreeDirectory(tree): if len(inspect.getfullargspec(tree.cscript['test']).args) == 3: - return tree.call('test', options, test) + return tree.call('test', tree.add_defaults(options), test) else: log_normal('Deprecated cscript test() method with no options parameter') return tree.call('test', test) @@ -570,47 +528,21 @@ class DockerTarget(Target): self.mounts.append(m) -class FlatpakTarget(Target): - def __init__(self, project, checkout): - super(FlatpakTarget, self).__init__('flatpak') - self.build_dependencies = False - self.project = project - self.checkout = checkout - - def setup(self): - pass - - def command(self, cmd): - command(cmd) - - def checkout_dependencies(self): - tree = globals.trees.get(self.project, self.checkout, self) - return tree.checkout_dependencies() - - def flatpak(self): - return 'flatpak' - - def flatpak_builder(self): - b = 'flatpak-builder' - if config.has('flatpak_state_dir'): - b += ' --state-dir=%s' % config.get('flatpak_state_dir') - return b - - class WindowsDockerTarget(DockerTarget): """ This target exposes the following additional API: - version: Windows version ('xp' or None) bits: bitness of Windows (32 or 64) name: name of our target e.g. x86_64-w64-mingw32.shared environment_prefix: path to Windows environment for the appropriate target (libraries and some tools) tool_path: path to 32- and 64-bit tools """ - def __init__(self, windows_version, bits, directory, environment_version): + def __init__(self, bits, directory, environment_version): super(WindowsDockerTarget, self).__init__('windows', directory) - self.version = windows_version self.bits = bits + # This was used to differentiate "normal" Windows from XP, and is no longer important, + # but old cscripts still look for it + self.version = None self.tool_path = '%s/usr/bin' % config.get('mxe_prefix') if self.bits == 32: @@ -675,17 +607,15 @@ class WindowsNativeTarget(Target): """ This target exposes the following additional API: - version: Windows version ('xp' or None) bits: bitness of Windows (32 or 64) name: name of our target e.g. x86_64-w64-mingw32.shared environment_prefix: path to Windows environment for the appropriate target (libraries and some tools) """ def __init__(self, directory): super().__init__('windows', directory) - self.version = None self.bits = 64 - self.environment_prefix = config.get('windows_native_environmnet_prefix') + self.environment_prefix = config.get('windows_native_environment_prefix') self.set('PATH', '%s/bin:%s' % (self.environment_prefix, os.environ['PATH'])) @@ -742,54 +672,67 @@ class AppImageTarget(LinuxTarget): self.privileged = True -def notarize_dmg(dmg, bundle_id): - p = subprocess.run( - ['xcrun', 'altool', '--notarize-app', '-t', 'osx', '-f', dmg, '--primary-bundle-id', bundle_id, '-u', config.get('apple_id'), '-p', config.get('apple_password'), '--output-format', 'xml'], - capture_output=True - ) - - def string_after(process, key): - lines = p.stdout.decode('utf-8').splitlines() - for i in range(0, len(lines)): - if lines[i].find(key) != -1: - return lines[i+1].strip().replace('', '').replace('', '') - - request_uuid = string_after(p, "RequestUUID") - if request_uuid is None: - print("Looking for upload ID") - message = string_after(p, "message") - print("Looking in %s" % message) - if message: - m = re.match('.*The upload ID is ([0-9a-f\-]*)', message) - if m: - request_uuid = m.groups()[0] - if request_uuid is None: - print("Response: %s" % p) - raise Error('No RequestUUID found in response from Apple') - - for i in range(0, 30): - print('%s: checking up on %s' % (datetime.datetime.now(), request_uuid)) - p = subprocess.run(['xcrun', 'altool', '--notarization-info', request_uuid, '-u', config.get('apple_id'), '-p', config.get('apple_password'), '--output-format', 'xml'], capture_output=True) - status = string_after(p, 'Status') - print('%s: got status %s' % (datetime.datetime.now(), status)) - if status == 'invalid': - raise Error("Notarization failed") - elif status == 'success': - subprocess.run(['xcrun', 'stapler', 'staple', dmg]) - return - elif status != "in progress": - print("Could not understand xcrun response") - print(p) - time.sleep(30) +class FlatpakTarget(Target): + def __init__(self, project, checkout, work): + super(FlatpakTarget, self).__init__('flatpak') + self.build_dependencies = False + self.project = project + self.checkout = checkout + # If we use git references we end up with a checkout in one mount trying + # to link to the git reference repo in other, which doesn't work. + globals.use_git_reference = False + if config.has('flatpak_state_dir'): + self.mount(config.get('flatpak_state_dir')) + + def command(self, c): + log_normal('host -> %s' % c) + command('%s %s' % (self.variables_string(), c)) - raise Error("Notarization timed out") + def setup(self): + super().setup() + globals.trees.get(self.project, self.checkout, self).checkout_dependencies() + + def flatpak(self): + return 'flatpak' + + def flatpak_builder(self): + b = 'flatpak-builder' + if config.has('flatpak_state_dir'): + b += ' --state-dir=%s' % config.get('flatpak_state_dir') + return b + + +def notarize_dmg(dmg): + p = subprocess.run( + config.get('osx_notarytool') + [ + 'submit', + '--apple-id', + config.get('apple_id'), + '--password', + config.get('apple_password'), + '--team-id', + config.get('apple_team_id'), + '--wait', + dmg + ], capture_output=True) + + last_line = [x.strip() for x in p.stdout.decode('utf-8').splitlines() if x.strip()][-1] + if last_line != 'status: Accepted': + print("Could not understand notarytool response") + print(p) + print(f"Last line: {last_line}") + raise Error('Notarization failed') + + subprocess.run(['xcrun', 'stapler', 'staple', dmg]) class OSXTarget(Target): - def __init__(self, directory=None): + def __init__(self, directory=None, environment_version=None): super(OSXTarget, self).__init__('osx', directory) self.sdk_prefix = config.get('osx_sdk_prefix') self.environment_prefix = config.get('osx_environment_prefix') + if environment_version: + self.environment_prefix += '_%s' % environment_version self.apple_id = config.get('apple_id') self.apple_password = config.get('apple_password') self.osx_keychain_file = config.get('osx_keychain_file') @@ -801,32 +744,37 @@ class OSXTarget(Target): def unlock_keychain(self): self.command('security unlock-keychain -p %s %s' % (self.osx_keychain_password, self.osx_keychain_file)) + def package(self, project, checkout, output_dir, options, notarize): + self.unlock_keychain() + tree = globals.trees.get(project, checkout, self) + with TreeDirectory(tree): + p = self._cscript_package_and_notarize(tree, options, self.can_notarize and notarize) + self._copy_packages(tree, p, output_dir) + def _copy_packages(self, tree, packages, output_dir): for p in packages: dest = os.path.join(output_dir, os.path.basename(devel_to_git(tree.commit, p))) copyfile(p, dest) - if os.path.exists(p + ".id"): - copyfile(p + ".id", dest + ".id") def _cscript_package_and_notarize(self, tree, options, notarize): """ Call package() in the cscript and notarize the .dmgs that are returned, if notarize == True """ - p = self._cscript_package(tree, options) - for x in p: - if not isinstance(x, tuple): - raise Error('macOS packages must be returned from cscript as tuples of (dmg-filename, bundle-id)') + output = [] + for x in self._cscript_package(tree, options): + # Some older cscripts give us the DMG filename and the bundle ID, even though + # (since using notarytool instead of altool for notarization) the bundle ID + # is no longer necessary. Cope with either type of cscript. + dmg = x[0] if isinstance(x, tuple) else x if notarize: - notarize_dmg(x[0], x[1]) - else: - with open(x[0] + '.id', 'w') as f: - print(x[1], file=f) - return [x[0] for x in p] + notarize_dmg(dmg) + output.append(dmg) + return output class OSXSingleTarget(OSXTarget): - def __init__(self, arch, sdk, deployment, directory=None, can_notarize=True): - super(OSXSingleTarget, self).__init__(directory) + def __init__(self, arch, sdk, deployment, directory=None, can_notarize=True, environment_version=None): + super(OSXSingleTarget, self).__init__(directory=directory, environment_version=environment_version) self.arch = arch self.sdk = sdk self.deployment = deployment @@ -835,10 +783,10 @@ class OSXSingleTarget(OSXTarget): flags = '-isysroot %s/MacOSX%s.sdk -arch %s' % (self.sdk_prefix, sdk, arch) if arch == 'x86_64': - host_enviro = '%s/x86_64/%s' % (config.get('osx_environment_prefix'), deployment) + host_enviro = '%s/x86_64/%s' % (self.environment_prefix, deployment) else: - host_enviro = '%s/x86_64/10.10' % config.get('osx_environment_prefix') - target_enviro = '%s/%s/%s' % (config.get('osx_environment_prefix'), arch, deployment) + host_enviro = '%s/x86_64/10.10' % self.environment_prefix + target_enviro = '%s/%s/%s' % (self.environment_prefix, arch, deployment) self.bin = '%s/bin' % target_enviro @@ -862,21 +810,19 @@ class OSXSingleTarget(OSXTarget): def package(self, project, checkout, output_dir, options, notarize): tree = self.build(project, checkout, options, for_package=True) - tree.add_defaults(options) - self.unlock_keychain() - p = self._cscript_package_and_notarize(tree, options, self.can_notarize and notarize) - self._copy_packages(tree, p, output_dir) + super().package(project, checkout, output_dir, options, notarize) class OSXUniversalTarget(OSXTarget): - def __init__(self, directory=None): - super(OSXUniversalTarget, self).__init__(directory) + def __init__(self, directory=None, environment_version=None): + super(OSXUniversalTarget, self).__init__(directory=directory, environment_version=environment_version) self.sdk = config.get('osx_sdk') self.sub_targets = [] for arch, deployment in (('x86_64', config.get('osx_intel_deployment')), ('arm64', config.get('osx_arm_deployment'))): - target = OSXSingleTarget(arch, self.sdk, deployment, os.path.join(self.directory, arch, deployment)) + target = OSXSingleTarget(arch, self.sdk, deployment, os.path.join(self.directory, arch, deployment), environment_version=environment_version) target.ccache = self.ccache self.sub_targets.append(target) + self.can_notarize = True def package(self, project, checkout, output_dir, options, notarize): for target in self.sub_targets: @@ -884,14 +830,16 @@ class OSXUniversalTarget(OSXTarget): tree.build_dependencies(options) tree.build(options, for_package=True) - self.unlock_keychain() - tree = globals.trees.get(project, checkout, self) - with TreeDirectory(tree): - p = self._cscript_package_and_notarize(tree, options, notarize) - self._copy_packages(tree, p, output_dir) + super().package(project, checkout, output_dir, options, notarize) + + @Target.ccache.setter + def ccache(self, v): + for target in self.sub_targets: + target.ccache = v + class SourceTarget(Target): - """Build a source .tar.bz2""" + """Build a source .tar.bz2 and .zst""" def __init__(self): super(SourceTarget, self).__init__('source') @@ -907,8 +855,34 @@ class SourceTarget(Target): with TreeDirectory(tree): name = read_wscript_variable(os.getcwd(), 'APPNAME') command('./waf dist') - p = os.path.abspath('%s-%s.tar.bz2' % (name, tree.version)) - copyfile(p, os.path.join(output_dir, os.path.basename(devel_to_git(tree.commit, p)))) + bz2 = os.path.abspath('%s-%s.tar.bz2' % (name, tree.version)) + copyfile(bz2, os.path.join(output_dir, os.path.basename(devel_to_git(tree.commit, bz2)))) + command('tar xjf %s' % bz2) + command('tar --zstd -cf %s-%s.tar.zst %s-%s' % (name, tree.version, name, tree.version)) + zstd = os.path.abspath('%s-%s.tar.zst' % (name, tree.version)) + copyfile(zstd, os.path.join(output_dir, os.path.basename(devel_to_git(tree.commit, zstd)))) + + +class LocalTarget(Target): + """Build on the local machine with its environment""" + def __init__(self, work, dependencies_only=False): + super(LocalTarget, self).__init__('linux', work, dependencies_only=dependencies_only) + # Hack around ffmpeg.git which sees that the target isn't windows/osx and then assumes + # distro will be there. + self.distro = None + self.detail = None + self.bits = 64 + self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig:%s/bin/pkgconfig' % (self.directory, self.directory)) + self.append_with_colon('LD_LIBRARY_PATH', '%s/lib' % self.directory) + self.set('CXXFLAGS', '-I%s/include' % self.directory) + self.set('LINKFLAGS', '-L%s/lib' % self.directory) + + def command(self, c): + log_normal('host -> %s' % c) + command('%s %s' % (self.variables_string(), c)) + + def cleanup(self): + rmtree(self.directory) # @param s Target string: # windows-{32,64} @@ -928,13 +902,10 @@ def target_factory(args): x = s.split('-') if platform.system() == "Windows": target = WindowsNativeTarget(args.work) + elif len(x) == 2: + target = WindowsDockerTarget(int(x[1]), args.work, args.environment_version) else: - if len(x) == 2: - target = WindowsDockerTarget(None, int(x[1]), args.work, args.environment_version) - elif len(x) == 3: - target = WindowsDockerTarget(x[1], int(x[2]), args.work, args.environment_version) - else: - raise Error("Bad Windows target name `%s'") + raise Error("Bad Windows target name `%s'") elif s.startswith('ubuntu-') or s.startswith('debian-') or s.startswith('centos-') or s.startswith('fedora-') or s.startswith('mageia-'): p = s.split('-') if len(p) != 3: @@ -948,17 +919,19 @@ def target_factory(args): elif s == 'raspbian': target = LinuxTarget(s, None, None, args.work) elif s == 'osx': - target = OSXUniversalTarget(args.work) + target = OSXUniversalTarget(args.work, environment_version=args.environment_version) elif s == 'osx-intel': - target = OSXSingleTarget('x86_64', config.get('osx_sdk'), config.get('osx_intel_deployment'), args.work) + target = OSXSingleTarget('x86_64', config.get('osx_sdk'), config.get('osx_intel_deployment'), args.work, environment_version=args.environment_version) elif s == 'osx-old': - target = OSXSingleTarget('x86_64', config.get('osx_sdk'), config.get('osx_old_deployment'), args.work, False) + target = OSXSingleTarget('x86_64', config.get('osx_sdk'), config.get('osx_old_deployment'), args.work, False, environment_version=args.environment_version) elif s == 'source': target = SourceTarget() elif s == 'flatpak': - target = FlatpakTarget(args.project, args.checkout) + target = FlatpakTarget(args.project, args.checkout, args.work) elif s == 'appimage': target = AppImageTarget(args.work) + elif s == 'local': + target = LocalTarget(args.work, args.dependencies_only) if target is None: raise Error("Bad target `%s'" % s) @@ -982,7 +955,7 @@ def target_factory(args): # Tree # -class Tree(object): +class Tree: """Description of a tree, which is a checkout of a project, possibly built. This class is never exposed to cscripts. Attributes: @@ -1013,7 +986,7 @@ class Tree(object): if globals.quiet: flags = '-q' redirect = '>/dev/null' - if config.has('git_reference'): + if config.has('git_reference') and globals.use_git_reference: ref = '--reference-if-able %s/%s.git' % (config.get('git_reference'), self.name) else: ref = '' @@ -1035,7 +1008,7 @@ class Tree(object): urls = command_and_read('git config --file .gitmodules --get-regexp url') for path, url in zip(paths, urls): ref = '' - if config.has('git_reference'): + if config.has('git_reference') and globals.use_git_reference: url = url.split(' ')[1] ref_path = os.path.join(config.get('git_reference'), os.path.basename(url)) if os.path.exists(ref_path): @@ -1043,19 +1016,11 @@ class Tree(object): path = path.split(' ')[1] command('git -c protocol.file.allow=always submodule --quiet update %s %s' % (ref, path)) - if os.path.exists('%s/wscript' % proj): - v = read_wscript_variable(proj, "VERSION"); - if v is not None: - try: - self.version = Version(v) - except: - try: - tag = command_and_read('git -C %s describe --match v* --tags' % proj)[0][1:] - self.version = Version.from_git_tag(tag) - except: - # We'll leave version as None if we can't read it; maybe this is a bad idea - # Should probably just install git on the Windows VM - pass + head_tag = command_and_read(f'git -C {proj} tag -l --points-at HEAD') + if head_tag: + self.version = head_tag[0][1:] + else: + self.version = command_and_read(f'git -C {proj} rev-parse --short HEAD')[0] os.chdir(cwd) @@ -1064,7 +1029,8 @@ class Tree(object): return self.cscript[function](self.target, *args) def add_defaults(self, options): - """Add the defaults from self into a dict options""" + """Add the defaults from self into a dict options and returns a new dict""" + new_options = copy.copy(options) if 'option_defaults' in self.cscript: from_cscript = self.cscript['option_defaults'] if isinstance(from_cscript, dict): @@ -1073,22 +1039,21 @@ class Tree(object): log_normal("Deprecated cscript option_defaults method; replace with a dict") defaults_dict = from_cscript() for k, v in defaults_dict.items(): - if not k in options: - options[k] = v + if not k in new_options: + new_options[k] = v + return new_options def dependencies(self, options): """ yield details of the dependencies of this tree. Each dependency is returned - as a tuple of (tree, options, parent_tree). The 'options' parameter are the options that - we want to force for 'self'. + as a tuple of (tree, options). + options: either from command line (for top-level tree) or from parent's dependencies() (for other trees) """ if not 'dependencies' in self.cscript: return if len(inspect.getfullargspec(self.cscript['dependencies']).args) == 2: - self_options = copy.copy(options) - self.add_defaults(self_options) - deps = self.call('dependencies', self_options) + deps = self.call('dependencies', self.add_defaults(options)) else: log_normal("Deprecated cscript dependencies() method with no options parameter") deps = self.call('dependencies') @@ -1101,7 +1066,7 @@ class Tree(object): dep_options = d[2] if len(d) > 2 else {} for i in dep.dependencies(dep_options): yield i - yield (dep, dep_options, self) + yield (dep, dep_options) def checkout_dependencies(self, options={}): for i in self.dependencies(options): @@ -1110,28 +1075,28 @@ class Tree(object): def build_dependencies(self, options): """ Called on the 'main' project tree (-p on the command line) to build all dependencies. - 'options' will be the ones from the command line. + options: either from command line (for top-level tree) or from parent's dependencies() (for other trees) """ - for i in self.dependencies(options): - i[0].build(i[1]) + for dependency, dependency_options in self.dependencies(options): + dependency.build(dependency_options) def build(self, options, for_package=False): + """ + options: either from command line (for top-level tree) or from parent's dependencies() (for other trees) + """ if self.built: return - log_verbose("Building %s %s %s with %s" % (self.name, self.commit_ish, self.version, options)) + log_verbose("Building %s %s %s with %s" % (self.name, self.commit_ish, self.version, self.add_defaults(options))) variables = copy.copy(self.target.variables) - options = copy.copy(options) - self.add_defaults(options) - if not globals.dry_run: num_args = len(inspect.getfullargspec(self.cscript['build']).args) if num_args == 3: - self.call('build', options, for_package) + self.call('build', self.add_defaults(options), for_package) elif num_args == 2: - self.call('build', options) + self.call('build', self.add_defaults(options)) else: self.call('build') @@ -1144,28 +1109,6 @@ class Tree(object): # def main(): - - commands = { - "build": "build project", - "package": "build and package the project", - "release": "release a project using its next version number (adding a tag)", - "pot": "build the project's .pot files", - "manual": "build the project's manual", - "doxygen": "build the project's Doxygen documentation", - "latest": "print out the latest version", - "test": "build the project and run its unit tests", - "shell": "start a shell in the project''s work directory", - "checkout": "check out the project", - "revision": "print the head git revision number", - "dependencies" : "print details of the project's dependencies as a .dot file" - } - - one_of = "" - summary = "" - for k, v in commands.items(): - one_of += "\t%s%s\n" % (k.ljust(20), v) - summary += k + " " - parser = argparse.ArgumentParser() parser.add_argument('-p', '--project', help='project name') parser.add_argument('-c', '--checkout', help='string to pass to git for checkout') @@ -1186,6 +1129,7 @@ def main(): subparsers = parser.add_subparsers(help='command to run', dest='command') parser_build = subparsers.add_parser("build", help="build project") + parser_build.add_argument('--dependencies-only', help='only build dependencies', action='store_true') parser_package = subparsers.add_parser("package", help="build and package project") parser_package.add_argument('--no-notarize', help='do not notarize .dmg packages', action='store_true') parser_release = subparsers.add_parser("release", help="release a project using its next version number (adding a tag)") @@ -1200,12 +1144,12 @@ def main(): parser_test = subparsers.add_parser("test", help="build the project and run its unit tests") parser_test.add_argument('--no-implicit-build', help='do not build first', action='store_true') parser_test.add_argument('--test', help="name of test to run, defaults to all") - parser_shell = subparsers.add_parser("shell", help="build the project then start a shell") + parser_shell = subparsers.add_parser("shell", help="start a shell in the project's work directory") parser_checkout = subparsers.add_parser("checkout", help="check out the project") parser_revision = subparsers.add_parser("revision", help="print the head git revision number") parser_dependencies = subparsers.add_parser("dependencies", help="print details of the project's dependencies as a .dot file") - parser_notarize = subparsers.add_parser("notarize", help="notarize .dmgs in a directory using *.dmg.id files") - parser_notarize.add_argument('--dmgs', help='directory containing *.dmg and *.dmg.id') + parser_notarize = subparsers.add_parser("notarize", help="notarize .dmgs in a directory") + parser_notarize.add_argument('--dmgs', help='directory containing *.dmg') global args args = parser.parse_args() @@ -1276,26 +1220,6 @@ def main(): if target is not None and not args.keep: target.cleanup() - elif args.command == 'release': - if args.minor is False and args.micro is False: - raise Error('you must specify --minor or --micro') - - target = SourceTarget() - tree = globals.trees.get(args.project, args.checkout, target) - - version = tree.version - version.to_release() - if args.minor: - version.bump_minor() - else: - version.bump_micro() - - with TreeDirectory(tree): - command('git tag -m "v%s" v%s' % (version, version)) - command('git push --tags') - - target.cleanup() - elif args.command == 'pot': target = SourceTarget() tree = globals.trees.get(args.project, args.checkout, target) @@ -1352,11 +1276,11 @@ def main(): if len(s) > 1: t = s[1] if len(t) > 0 and t[0] == 'v': - v = Version(t[1:]) - if (args.major is None or v.major == args.major) and (args.minor is None or v.minor == args.minor): + v = t[1:].split('.') + if (args.major is None or int(v[0]) == args.major) and (args.minor is None or int(v[1]) == args.minor): latest = v - print(latest) + print('.'.join(latest)) target.cleanup() elif args.command == 'test': @@ -1402,31 +1326,12 @@ def main(): shutil.copytree('.', args.output) target.cleanup() - elif args.command == 'dependencies': - if args.target is None: - raise Error('you must specify -t or --target') - if args.checkout is None: - raise Error('you must specify -c or --checkout') - - target = target_factory(args) - tree = globals.trees.get(args.project, args.checkout, target) - print("strict digraph {") - for d in list(tree.dependencies({})): - print("%s -> %s;" % (d[2].name.replace("-", "-"), d[0].name.replace("-", "_"))) - print("}") - elif args.command == 'notarize': if args.dmgs is None: raise Error('you must specify ---dmgs') for dmg in Path(args.dmgs).glob('*.dmg'): - id = None - try: - with open(str(dmg) + '.id') as f: - id = f.readline().strip() - except OSError: - raise Error('could not find ID file for %s' % dmg) - notarize_dmg(dmg, id) + notarize_dmg(dmg) try: main()