Esempio n. 1
0
    def dump_yaml(s, build_dir):

        # Enable dumping multiline strings as block literals
        #
        # Block literals look like this:
        #
        #     foo: |
        #       line 1
        #       line 2
        #       line 3
        #
        # The indentation of the first line is used as the common indentation
        # for the entire block, so when read as a string it looks like this:
        #
        #     >>> line 1
        #     >>> line 2
        #     >>> line 3
        #

        def str_representer(dumper, data):
            tmp = {'tag': 'tag:yaml.org,2002:str', 'value': data}
            if len(data.splitlines()) > 1:
                tmp.update({'style': '|'})
            return dumper.represent_scalar(**tmp)

        yaml.add_representer(str, str_representer)

        # Dump the content

        write_yaml(
            data=s._config,
            path=build_dir + '/configure.yml',
        )
Esempio n. 2
0
 def save_construct_path(s, construct_path):
     yaml_path = '.mflowgen.yml'
     try:
         data = read_yaml(yaml_path)
     except Exception:
         data = {}
     data['construct'] = construct_path
     write_yaml(data=data, path=yaml_path)
Esempio n. 3
0
 def update_stash( s ):
   write_yaml(
     data = s.stash,
     path = s.stash_yaml_path,
   )
Esempio n. 4
0
 def set_stash_path( s, path ):
   s.link_path = path
   write_yaml(
     data = { 'path' : s.link_path },
     path = s.link_path_yaml,
   )
Esempio n. 5
0
    def launch_push(s, help_, step, msg, all_):

        try:
            author = os.environ['USER']
        except KeyError:
            author = 'Unknown'

        # Help message

        def print_help():
            print()
            print(bold('Usage:'), 'mflowgen stash push',
                  '--step/-s <int> --message/-m "<str>"', '[--all]')
            print()
            print(bold('Example:'), 'mflowgen stash push',
                  '--step 5 -m "foo bar"')
            print()
            print('Pushes a built step to the mflowgen stash. The given step')
            print('is copied to the stash, preserving all permissions and')
            print('following all symlinks. By default, only the outputs of a')
            print('step are stashed, but the entire step can be stashed with')
            print('--all. The stashed copy is given a hash stamp and is')
            print('marked as authored by $USER (' + author + '). An optional')
            print('message can also be attached to each push.')
            print()

        if help_ or step == None or not msg:
            print_help()
            return

        # Sanity-check the stash

        s.verify_stash()

        # Get step to push
        #
        # Check the current directory and search for a dirname matching the
        # given step number.
        #
        # - E.g., if "step" is 5 then look for any sub-directory that starts
        #   with "5-". If there is a directory called
        #   '4-synopsys-dc-synthesis' then "step_name" will be
        #   "synopsys-dc-synthesis"
        #

        build_dirs = os.listdir('.')
        targets = [_ for _ in build_dirs if _.startswith(str(step) + '-')]

        try:
            push_target = targets[0]
        except IndexError:
            print(bold('Error:'), 'No build directory found for step',
                  '{}'.format(step))
            sys.exit(1)

        # Create a unique name for the stashed copy of this step

        today = datetime.today()
        datestamp = datetime.strftime(today, '%Y-%m%d')
        hashstamp = s.gen_unique_hash()
        step_name = '-'.join(push_target.split('-')[1:])

        dst_dirname = '-'.join([datestamp, step_name, hashstamp])

        # Try to get some information to help describe "where this step came
        # from"

        def get_shell_output(cmd):
            try:
                output = subprocess.check_output(cmd.split(),
                                                 stderr=subprocess.DEVNULL,
                                                 universal_newlines=True)
                output = output.strip()
            except Exception:
                output = ''
            return output

        def get_hostname():
            import socket
            return socket.gethostname()

        git_cmd = 'git rev-parse --short HEAD'  # git commit hash
        git_hash = get_shell_output(git_cmd)

        git_cmd = 'git rev-parse --show-toplevel'  # git root dir
        git_repo = get_shell_output(git_cmd)
        git_repo = os.path.basename(git_repo)

        build_path = os.getcwd()  # build dir path
        hostname = get_hostname()  # hostname

        stashed_from = {
            'stashed-from-git-root-dir': git_repo,
            'stashed-from-git-root-dir-hash': git_hash,
            'stashed-from-dir': build_path,
            'stashed-from-hostname': hostname,
        }

        # Helper function to ignore copying files other than the outputs

        def f_ignore(path, files):
            # For nested subdirectories, ignore all files
            if '/' in path:
                if path.split('/')[1] == 'outputs':
                    return []  # ignore nothing in outputs
                elif path.split('/')[1] in ['logs', 'reports']:  # TEMPORARY
                    return []  # ignore nothing                  # TEMPORARY
                else:
                    return files  # ignore everything for any other directory
            # At the top level, keep the outputs and a few other misc files
            keep = [
                'outputs',
                'logs',  # TEMPORARY
                'reports',  # TEMPORARY
                'configure.yml',
                'mflowgen-run.log',
                '.time_start',
                '.time_end',
                '.stamp',
                '.execstamp',
                '.postconditions.stamp',
            ]
            ignore = [_ for _ in files if _ not in keep]
            return ignore

        # Now copy src to dst
        #
        # - symlinks                 = False  # Follow all symlinks
        # - ignore_dangling_symlinks = False  # Stop with error if we cannot
        #                                     #  follow a link to something
        #                                     #  we need
        # - ignore                   = (func) # Ignore all but the outputs
        #                                     #  unless "--all" was given
        #

        remote_path = s.get_stash_path() + '/' + dst_dirname

        try:
            shutil.copytree(src=push_target,
                            dst=remote_path,
                            symlinks=False,
                            ignore=None if all_ else f_ignore,
                            ignore_dangling_symlinks=False)
        except shutil.Error:
            # According to online discussion, ignore_dangling_symlinks does not
            # apply recursively within sub-directories (a bug):
            #
            # - https://bugs.python.org/issue38523
            #
            # But according to more online discussion, copytree finishes copying
            # everything else before raising the exception.
            #
            # - https://bugs.python.org/issue6547
            #
            # So if we just pass here, we will have everything except dangling
            # symlinks, which is fine for our use case. Dangling symlinks can
            # happen in a few situations:
            #
            #  1. In inputs, if users cleaned earlier dependent steps. In this
            #  situation, we are just doing our best to copy what is available.
            #
            #  2. Some symlink to somewhere we do not have permission to view.
            #  It would be nice to raise an exception in this case, but that is
            #  hard to differentiate.
            #
            pass
        except Exception as e:
            print(bold('Error:'), 'Failed to complete stash push')
            shutil.rmtree(path=remote_path, ignore_errors=True)  # clean up
            raise

        # Update the metadata in the stash

        push_metadata = {
            'date': datestamp,
            'dir': dst_dirname,
            'hash': hashstamp,
            'author': author,
            'step': step_name,
            'msg': msg,
            'stashed-from': stashed_from,
        }

        s.stash.append(push_metadata)
        s.update_stash()

        # Try adding the metadata to the stashed step itself, so that when the
        # step gets pulled somewhere, we have all of its metadata to know
        # where it came from

        try:
            data = push_metadata
            data.update({'stash-dir': s.link_path})  # add stash dir
            write_yaml(
                data=data,
                path=remote_path + '/.mflowgen.stash.node.yml',
            )
        except Exception as e:
            pass

        print('Stashed step {step} "{step_name}" as author "{author}"'.format(
            step=step,
            step_name=step_name,
            author=author,
        ))