r'''
    Compile a config from a PowerShell script (``.ps1``)

    Args:

        path (str): Path (local) to the script that will create the ``.mof``
            configuration file. If no source is passed, the file must exist
            locally. Required.

        source (str): Path to the script on ``file_roots`` to cache at the
            location specified by ``path``. The source file will be cached
            locally and then executed. If source is not passed, the config
            script located at ``path`` will be compiled. Optional.

        config_name (str): The name of the Configuration within the script to
            apply. If the script contains multiple configurations within the
            file a ``config_name`` must be specified. If the ``config_name`` is
            not specified, the name of the file will be used as the
            ``config_name`` to run. Optional.

        config_data (str): Configuration data in the form of a hash table that
            will be passed to the ``ConfigurationData`` parameter when the
            ``config_name`` is compiled. This can be the path to a ``.psd1``
            file containing the proper hash table or the PowerShell code to
            create the hash table.

            .. versionadded:: 2017.7.0

        config_data_source (str): The path to the ``.psd1`` file on
            ``file_roots`` to cache at the location specified by
            ``config_data``. If this is specified, ``config_data`` must be a
            local path instead of a hash table.

            .. versionadded:: 2017.7.0

        script_parameters (str): Any additional parameters expected by the
            configuration script. These must be defined in the script itself.

            .. versionadded:: 2017.7.0

        salt_env (str): The salt environment to use when copying the source.
            Default is 'base'

    Returns:
        dict: A dictionary containing the results of the compilation

    CLI Example:

    To compile a config from a script that already exists on the system:

    .. code-block:: bash

        salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1

    To cache a config script to the system from the master and compile it:

    .. code-block:: bash

        salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1

def compile_config(path,
                   source=None,
                   config_name=None,
                   config_data=None,
                   config_data_source=None,
                   script_parameters=None,
                   salt_env='base'):
    r'''
    Compile a config from a PowerShell script (``.ps1``)

    Args:

        path (str): Path (local) to the script that will create the ``.mof``
            configuration file. If no source is passed, the file must exist
            locally. Required.

        source (str): Path to the script on ``file_roots`` to cache at the
            location specified by ``path``. The source file will be cached
            locally and then executed. If source is not passed, the config
            script located at ``path`` will be compiled. Optional.

        config_name (str): The name of the Configuration within the script to
            apply. If the script contains multiple configurations within the
            file a ``config_name`` must be specified. If the ``config_name`` is
            not specified, the name of the file will be used as the
            ``config_name`` to run. Optional.

        config_data (str): Configuration data in the form of a hash table that
            will be passed to the ``ConfigurationData`` parameter when the
            ``config_name`` is compiled. This can be the path to a ``.psd1``
            file containing the proper hash table or the PowerShell code to
            create the hash table.

            .. versionadded:: 2017.7.0

        config_data_source (str): The path to the ``.psd1`` file on
            ``file_roots`` to cache at the location specified by
            ``config_data``. If this is specified, ``config_data`` must be a
            local path instead of a hash table.

            .. versionadded:: 2017.7.0

        script_parameters (str): Any additional parameters expected by the
            configuration script. These must be defined in the script itself.

            .. versionadded:: 2017.7.0

        salt_env (str): The salt environment to use when copying the source.
            Default is 'base'

    Returns:
        dict: A dictionary containing the results of the compilation

    CLI Example:

    To compile a config from a script that already exists on the system:

    .. code-block:: bash

        salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1

    To cache a config script to the system from the master and compile it:

    .. code-block:: bash

        salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1
    '''
    if source:
        log.info('DSC: Caching %s', source)
        cached_files = __salt__['cp.get_file'](path=source,
                                               dest=path,
                                               saltenv=salt_env,
                                               makedirs=True)
        if not cached_files:
            error = 'Failed to cache {0}'.format(source)
            log.error('DSC: %s', error)
            raise CommandExecutionError(error)

    if config_data_source:
        log.info('DSC: Caching %s', config_data_source)
        cached_files = __salt__['cp.get_file'](path=config_data_source,
                                               dest=config_data,
                                               saltenv=salt_env,
                                               makedirs=True)
        if not cached_files:
            error = 'Failed to cache {0}'.format(config_data_source)
            log.error('DSC: %s', error)
            raise CommandExecutionError(error)

    # Make sure the path exists
    if not os.path.exists(path):
        error = '"{0}" not found'.format(path)
        log.error('DSC: %s', error)
        raise CommandExecutionError(error)

    if config_name is None:
        # If the name of the config isn't passed, make it the name of the .ps1
        config_name = os.path.splitext(os.path.basename(path))[0]

    cwd = os.path.dirname(path)

    # Run the script and see if the compile command is in the script
    cmd = [path]
    # Add any script parameters
    if script_parameters:
        cmd.append(script_parameters)
    # Select fields to return
    cmd.append('| Select-Object -Property FullName, Extension, Exists, '
               '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) '
               '-Format g}}')

    cmd = ' '.join(cmd)

    ret = _pshell(cmd, cwd)

    if ret:
        # Script compiled, return results
        if ret.get('Exists'):
            log.info('DSC: Compile Config: %s', ret)
            return ret

    # If you get to this point, the script did not contain a compile command
    # dot source the script to compile the state and generate the mof file
    cmd = ['.', path]
    if script_parameters:
        cmd.append(script_parameters)
    cmd.extend([';', config_name])
    if config_data:
        cmd.append(config_data)
    cmd.append('| Select-Object -Property FullName, Extension, Exists, '
               '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) '
               '-Format g}}')

    cmd = ' '.join(cmd)

    ret = _pshell(cmd, cwd)

    if ret:
        # Script compiled, return results
        if ret.get('Exists'):
            log.info('DSC: Compile Config: %s', ret)
            return ret

    error = 'Failed to compile config: {0}'.format(path)
    error += '\nReturned: {0}'.format(ret)
    log.error('DSC: %s', error)
    raise CommandExecutionError(error)