# coding=utf-8
"""
    crumhorn configuration bundler.

    Raw configurations ("package specification") are read in, and bundled into a "Machine Spec", which can be handled by the other crumhorn tools (e.g. prepare and launch)

    Usage:
        compile <package> [--from=<base_configuration>]
        compile <package> <destination> [--from=<base_configuration>]

    Arguments:
        <package>                       The folder containing your package specification. This is where your horn.toml
                                        lives.
        <destination>                   The destination for your bundled Machine Spec.

    Options:
        --from <base_configuration>     An already-compiled Machine Spec. If specified, when this package is prepared,
                                        then first boot will be from a snapshot of that package, rather than a raw image.

"""

import base64
import hashlib
import os
import re
import sys
import tarfile
from os import path
from tempfile import NamedTemporaryFile, TemporaryDirectory

import docopt

from crumhorn.configuration import machineconfiguration

_missing_data_pattern = re.compile('filename \'(?P<filename>[^\']+)\' not found')


class MissingDataFileError(RuntimeError):
    pass


def compile_folder(source, output, base_package=None):
    source = path.abspath(source)
    output = path.abspath(output)
    with tarfile.open(output, mode='w:xz') as dest:
        lock_hash = hashlib.sha512()
        for dirpath, dirs, files in os.walk(source, followlinks=True):
            # these sorts ensure stable hashing for unchanged content
            dirs[:] = sorted(dirs)
            for f in sorted(files):
                f_path = path.join(dirpath, f)
                with open(f_path, 'rb') as opened:
                    lock_hash.update(opened.read())

                file_to_add = path.abspath(f_path)
                dest_location = path.relpath(file_to_add, source)
                dest.add(file_to_add, arcname=dest_location)

        if base_package is not None:
            with TemporaryDirectory() as tempdir:
                with tarfile.open(base_package, mode='r:xz') as base:
                    base.extractall(path=tempdir)
                    with open(path.join(tempdir, 'horn.toml'), 'rb') as base_hash:
                        lock_hash.update(base_hash.read())
                dest.add(tempdir, arcname='base')

        with NamedTemporaryFile(mode='wb') as lock_file:
            lock = base64.urlsafe_b64encode(lock_hash.digest())
            lock_file.file.write(lock)
            lock_file.file.close()
            dest.add(lock_file.name, arcname='lock')

    # Check the resultant machinespec can load
    try:
        result = machineconfiguration.load_configuration(output)
    except KeyError as e:
        for a in e.args:
            missing_file_match = _missing_data_pattern.search(a)
            if missing_file_match:
                raise MissingDataFileError(missing_file_match.group('filename'))


def main(argv=None):
    argv = argv if argv is not None else sys.argv[1:]
    options = docopt.docopt(__doc__, argv=argv)
    source = options['<package>']
    dest = options['<destination>']
    if not dest:
        dest = path.basename(source) + '.crumspec'
    base_package = options['--from']
    compile_folder(source, dest, base_package)



if __name__ == '__main__':
    main()

@@ -922,18 +922,8 @@
 lib%0A
-import os%0A
 impo
@@ -1048,16 +1048,28 @@
 t docopt
+%0Aimport toml
 %0A%0Afrom c
@@ -1234,15 +1234,11 @@
 ror(
-Runtime
+Key
 Erro
@@ -1252,16 +1252,168 @@
  pass%0A%0A%0A
+def _files_from_configuration(config_dict):%0A    return %5Bf%5B'local'%5D for f in config_dict.get(config_dict%5B'horn'%5D.get('name', %7B%7D), %7B%7D).get('files', %5B%5D)%5D%0A%0A
 def comp
@@ -1531,193 +1531,84 @@
 ut)%0A
+%0A
     
-with tarfile.open(output, mode='w:xz') as dest:%0A        lock_hash = hashlib.sha512()%0A        for dirpath, dirs, files in os.walk(source, followlinks=True):%0A            # these
+configuration = toml.load(path.join(source, 'horn.toml'))%0A    #
  sort
-s
+ to
  ens
@@ -1656,43 +1656,178 @@
     
-        dirs%5B:%5D = sorted(dirs)%0A    
+local_files = sorted(_files_from_configuration(configuration) + %5B'horn.toml'%5D)%0A%0A    with tarfile.open(output, mode='w:xz') as dest:%0A        lock_hash = hashlib.sha512()%0A%0A
     
@@ -1843,27 +1843,21 @@
  in 
-sorted(
+local_
 files
-)
 :%0A
-    
     
@@ -1887,19 +1887,35 @@
 oin(
-dirpath, f)
+source, f)%0A            try:
 %0A   
@@ -2014,21 +2014,101 @@
 read())%0A
-%0A
     
+        except FileNotFoundError:%0A                raise MissingDataFileError(f)%0A%0A
         
@@ -2146,20 +2146,16 @@
 f_path)%0A
-    
         
@@ -2208,20 +2208,16 @@
 source)%0A
-    
         
@@ -2969,30 +2969,8 @@
 %0A   
- try:%0A        result =
  mac
@@ -3018,240 +3018,8 @@
 ut)%0A
-    except KeyError as e:%0A        for a in e.args:%0A            missing_file_match = _missing_data_pattern.search(a)%0A            if missing_file_match:%0A                raise MissingDataFileError(missing_file_match.group('filename'))%0A
 %0A%0Ade
@@ -3363,17 +3363,16 @@
 kage)%0A%0A%0A
-%0A
 if __nam

Move checking of localfiles' presence to the build-archive step, and find them by loading the horn.toml.