Try to fix option parsing and add -n.
[cdist.git] / cdist
diff --git a/cdist b/cdist
index 3aa09a3c62fd2e55e4e9a276b55719ab879221f7..75230b4b52551579b513a73353b592e6897db287 100755 (executable)
--- a/cdist
+++ b/cdist
@@ -306,11 +306,26 @@ class Version:
 
 class Target(object):
     """
-    platform -- platform string (e.g. 'windows', 'linux', 'osx')
-    directory -- directory to work in; if None we will use a temporary directory
-    Temporary directories will be removed after use; specified directories will not.
+    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:
+
+    platform: platform string (e.g. 'windows', 'linux', 'osx')
+    parallel: number of parallel jobs to run
+    directory: directory to work in
+    variables: dict of environment variables
+    debug: True to build a debug version, otherwise False
+    set(a, b): set the value of variable 'a' to 'b'
+    unset(a): unset the value of variable 'a'
+    command(c): run the command 'c' in the build environment
+
     """
+
     def __init__(self, platform, directory=None):
+        """
+        platform -- platform string (e.g. 'windows', 'linux', 'osx')
+        directory -- directory to work in; if None we will use a temporary directory
+        Temporary directories will be removed after use; specified directories will not.
+        """
         self.platform = platform
         self.parallel = int(config.get('parallel'))
 
@@ -328,14 +343,14 @@ class Target(object):
 
     def package(self, project, checkout):
         tree = globals.trees.get(project, checkout, self)
-        tree.build_dependencies()
-        tree.build(tree)
+        tree.build_dependencies(args.dry_run)
+        tree.build(args.dry_run)
         return tree.call('package', tree.version), tree.git_commit
 
     def test(self, tree, test):
         """test is the test case to run, or None"""
-        tree.build_dependencies()
-        tree.build()
+        tree.build_dependencies(args.dry_run)
+        tree.build(args.dry_run)
         return tree.call('test', test)
 
     def set(self, a, b):
@@ -369,19 +384,24 @@ class Target(object):
         if self.rmdir:
             rmtree(self.directory)
 
-#
-# Windows
-#
 
 class WindowsTarget(Target):
+    """
+    This target exposes the following additional API:
+
+    version: Windows version ('xp' or None)
+    bits: bitness of Windows (32 or 64)
+    environment_prefix: path to Windows environment
+    mingw_path: path to mingw binaries
+    """
     def __init__(self, version, bits, directory=None):
         super(WindowsTarget, self).__init__('windows', directory)
         self.version = version
         self.bits = bits
 
-        self.windows_prefix = '%s/%d' % (config.get('windows_environment_prefix'), self.bits)
-        if not os.path.exists(self.windows_prefix):
-            raise Error('windows prefix %s does not exist' % self.windows_prefix)
+        self.environment_prefix = '%s/%d' % (config.get('windows_environment_prefix'), self.bits)
+        if not os.path.exists(self.environment_prefix):
+            raise Error('environment prefix %s does not exist' % self.environment_prefix)
 
         if self.bits == 32:
             self.mingw_name = 'i686'
@@ -391,16 +411,16 @@ class WindowsTarget(Target):
         self.mingw_path = '%s/%d/bin' % (config.get('mingw_prefix'), self.bits)
         self.mingw_prefixes = ['/%s/%d' % (config.get('mingw_prefix'), self.bits), '%s/%d/%s-w64-mingw32' % (config.get('mingw_prefix'), bits, self.mingw_name)]
 
-        self.set('PKG_CONFIG_LIBDIR', '%s/lib/pkgconfig' % self.windows_prefix)
+        self.set('PKG_CONFIG_LIBDIR', '%s/lib/pkgconfig' % self.environment_prefix)
         self.set('PKG_CONFIG_PATH', '%s/lib/pkgconfig:%s/bin/pkgconfig' % (self.directory, self.directory))
-        self.set('PATH', '%s/bin:%s:%s' % (self.windows_prefix, self.mingw_path, os.environ['PATH']))
+        self.set('PATH', '%s/bin:%s:%s' % (self.environment_prefix, self.mingw_path, os.environ['PATH']))
         self.set('CC', '%s-w64-mingw32-gcc' % self.mingw_name)
         self.set('CXX', '%s-w64-mingw32-g++' % self.mingw_name)
         self.set('LD', '%s-w64-mingw32-ld' % self.mingw_name)
         self.set('RANLIB', '%s-w64-mingw32-ranlib' % self.mingw_name)
         self.set('WINRC', '%s-w64-mingw32-windres' % self.mingw_name)
-        cxx = '-I%s/include -I%s/include' % (self.windows_prefix, self.directory)
-        link = '-L%s/lib -L%s/lib' % (self.windows_prefix, self.directory)
+        cxx = '-I%s/include -I%s/include' % (self.environment_prefix, self.directory)
+        link = '-L%s/lib -L%s/lib' % (self.environment_prefix, self.directory)
         for p in self.mingw_prefixes:
             cxx += ' -I%s/include' % p
             link += ' -L%s/lib' % p
@@ -409,12 +429,23 @@ class WindowsTarget(Target):
         self.set('LINKFLAGS', '"%s"' % link)
         self.set('LDFLAGS', '"%s"' % link)
 
+        # This is for backwards-compatibility
+        self.windows_prefix = self.environment_prefix
+
     def command(self, c):
         log('host -> %s' % c)
         command('%s %s' % (self.variables_string(), c))
 
 class LinuxTarget(Target):
-    """Parent for Linux targets"""
+    """
+    Parent for Linux targets.
+    This target exposes the following additional API:
+
+    distro: distribution ('debian', 'ubuntu', 'centos' or 'fedora')
+    version: distribution version (e.g. '12.04', '8', '6.5')
+    bits: bitness of the distribution (32 or 64)
+    """
+
     def __init__(self, distro, version, bits, directory=None):
         super(LinuxTarget, self).__init__('linux', directory)
         self.distro = distro
@@ -452,15 +483,34 @@ class HostTarget(LinuxTarget):
     def command(self, c):
         command('%s %s' % (self.variables_string(), c))
 
-#
-# OS X
-#
+
+class DockerTarget(Target):
+    """
+    Build a Docker image.
+
+    This target exposes the following additional API:
+
+    deb: path to Debian 8 .deb
+    """
+    def __init__(self, directory=None):
+        super(DockerTarget, self).__init__('docker', directory)
+        self.debian = ChrootTarget('debian', '8', 64, directory)
+
+    def command(self, c):
+        log('host -> %s' % c)
+        command('%s %s' % (self.variables_string(), c))
+
+    def package(self, project, checkout):
+        self.deb = self.debian.package(project, checkout)
+        return globals.trees.get(project, checkout, self).call('package', tree.version), tree.git_commit
+
 
 class OSXTarget(Target):
     def __init__(self, directory=None):
         super(OSXTarget, self).__init__('osx', directory)
         self.sdk = config.get('osx_sdk')
         self.sdk_prefix = config.get('osx_sdk_prefix')
+        self.environment_prefix = config.get('osx_environment_prefix')
 
     def command(self, c):
         command('%s %s' % (self.variables_string(False), c))
@@ -502,8 +552,8 @@ class OSXUniversalTarget(OSXTarget):
         for b in [32, 64]:
             target = OSXSingleTarget(b, os.path.join(self.directory, '%d' % b))
             tree = globals.trees.get(project, checkout, target)
-            tree.build_dependencies()
-            tree.build()
+            tree.build_dependencies(False)
+            tree.build(False)
 
         tree = globals.trees.get(project, checkout, self)
         with TreeDirectory(tree):
@@ -582,6 +632,8 @@ def target_factory(s, debug, work):
             target = OSXUniversalTarget(work)
     elif s == 'source':
         target = SourceTarget()
+    elif s == 'docker':
+        target = DockerTarget()
 
     if target is None:
         raise Error("Bad target `%s'" % s)
@@ -649,34 +701,42 @@ class Tree(object):
         with TreeDirectory(self):
             return self.cscript[function](self.target, *args)
 
-    def build_dependencies(self):
+    def build_dependencies(self, dry_run, options=None):
         if 'dependencies' in self.cscript:
-            for d in self.cscript['dependencies'](self.target):
+            if len(inspect.getargspec(self.cscript['dependencies']).args) == 2:
+                deps = self.call('dependencies', options)
+            else:
+                deps = self.call('dependencies')
+
+            for d in deps:
                 log('Building dependency %s %s of %s' % (d[0], d[1], self.name))
                 dep = globals.trees.get(d[0], d[1], self.target)
-                dep.build_dependencies()
 
                 # Make the options to pass in from the option_defaults of the thing
                 # we are building and any options specified by the parent.
-                options = {}
+
                 if 'option_defaults' in dep.cscript:
-                    options = dep.cscript['option_defaults']()
-                    if len(d) > 2:
-                        for k, v in d[2].items():
-                            options[k] = v
+                    for k, v in dep.cscript['option_defaults'].items():
+                        options[k] = v
+
+                if len(d) > 2:
+                    for k, v in d[2].items():
+                        options[k] = v
 
-                dep.build(options)
+                dep.build_dependencies(dry_run, options)
+                dep.build(dry_run, options)
 
-    def build(self, options=None):
+    def build(self, dry_run, options=None):
         if self.built:
             return
 
         variables = copy.copy(self.target.variables)
 
-        if len(inspect.getargspec(self.cscript['build']).args) == 2:
-            self.call('build', options)
-        else:
-            self.call('build')
+        if not dry_run:
+            if len(inspect.getargspec(self.cscript['build']).args) == 2:
+                self.call('build', options)
+            else:
+                self.call('build')
 
         self.target.variables = variables
         self.built = True
@@ -723,6 +783,7 @@ def main():
     parser.add_argument('-w', '--work', help='override default work directory')
     parser.add_argument('-g', '--git-prefix', help='override configured git prefix')
     parser.add_argument('--test', help='name of test to run (with `test''), defaults to all')
+    parser.add_argument('-n', '--dry-run', help='run the process without building anything')
     args = parser.parse_args()
 
     # Override configured stuff
@@ -757,8 +818,8 @@ def main():
 
         target = target_factory(args.target, args.debug, args.work)
         tree = globals.trees.get(args.project, args.checkout, target)
-        tree.build_dependencies()
-        tree.build()
+        tree.build_dependencies(dry_run)
+        tree.build(dry_run)
         if not args.keep:
             target.cleanup()