예제 #1
0
def genloc(config):
    """ Generate localization """
    print_action('Generating Localization')
    cmd_args = [
        config.uproject_file_path, '-Run=GatherText',
        '-config={}'.format(config.proj_localization_script), '-log'
    ]
    if launch(config.UE4EditorPath, cmd_args) != 0:
        error_exit('Failed to generate localization, see errors...')

    click.pause()
예제 #2
0
def compile_all_blueprints(config: ProjectConfig):
    print_action('Compiling All Blueprints')
    cmd_args = [
        config.uproject_file_path, '-run=CompileAllBlueprints',
        '-autocheckout', '-projectonly', '-unattended'
    ]
    if launch(config.UE4EditorPath, cmd_args) != 0:
        error_exit('Failed to compile all blueprints, see errors...',
                   not config.automated)

    if not config.automated:
        click.pause()
예제 #3
0
def fix_redirects(config: ProjectConfig):
    print_action('Fixing Redirectors')
    cmd_args = [
        config.uproject_file_path, '-run=ResavePackages', '-fixupredirects',
        '-autocheckout', '-projectonly', '-unattended'
    ]
    if launch(config.UE4EditorPath, cmd_args) != 0:
        error_exit('Failed to fixup redirectors, see errors...',
                   not config.automated)

    if not config.automated:
        click.pause()
예제 #4
0
def genproj(config):
    """ Generate project file """
    print_action('Generating Project Files')

    cmd_args = [
        '-ProjectFiles', '-project={}'.format(config.uproject_file_path),
        '-game', '-engine'
    ]
    if get_visual_studio_version() == 2017:
        cmd_args.append('-2017')
    if launch(config.UE4UBTPath, cmd_args) != 0:
        error_exit('Failed to generate project files, see errors...')
예제 #5
0
def client(config: ProjectConfig, extra, ip):
    """ Run the client """
    print_action('Running Client')
    cmd_args = ['-game', '-windowed', '-ResX=1280', '-ResY=720']
    cmd_args.extend(['-' + arg.strip() for arg in extra.split('-')[1:]])

    if ip != '':
        cmd_args.insert(0, ip)

    client_exe_path = os.path.join(
        config.uproject_dir_path, 'builds\\WindowsNoEditor\\{0}\\Binaries\\'
        'Win64\\{0}.exe'.format(config.uproject_name))
    if not os.path.isfile(client_exe_path):
        error_exit('Client is not built!', not config.automated)

    launch(client_exe_path, cmd_args, True, should_wait=False)
예제 #6
0
def server(config: ProjectConfig, extra, umap):
    """ Run a server """
    print_action('Running Server')
    cmd_args = []
    cmd_args.extend(['-' + arg.strip() for arg in extra.split('-')[1:]])

    if umap != '':
        cmd_args.insert(0, umap)

    server_exe_path = os.path.join(
        config.uproject_dir_path, 'builds\\WindowsServer\\{0}\\Binaries\\'
        'Win64\\{0}Server.exe'.format(config.uproject_name))
    if not os.path.isfile(server_exe_path):
        error_exit('Server is not built!', not config.automated)

    launch(server_exe_path, cmd_args, True, should_wait=False)
예제 #7
0
def setup_perforce_creds(config: ProjectConfig):
    if shutil.which("p4") is None:
        error_exit(
            'Perforce was not found on the path. Make sure perforce is installed and on your systems path.',
            not config.automated)

    if os.path.isfile('p4config.txt'):
        result = click.confirm('Credentials already set. Overwrite them?',
                               default=False)
        if not result:
            return

    with open('p4config.txt', 'w') as p4_file:
        user_name = click.prompt('Type User Name')
        if user_name is None:
            return

        client_name = click.prompt('Type Workspace Name')
        if client_name is None:
            return

        server_name = click.prompt(
            'Type Server Address ex: ssl:127.0.0.1:1666')
        if server_name is None:
            return

        p4_file.writelines([
            'P4USER={}\n'.format(user_name),
            'P4CLIENT={}\n'.format(client_name),
            'P4PORT={}'.format(server_name)
        ])

    try:
        subprocess.check_output(["p4", "set", "P4CONFIG=p4config.txt"])
        result = subprocess.run(["p4", "client", "-o"],
                                stdout=subprocess.PIPE,
                                timeout=4,
                                check=False).stdout.decode("utf-8")
        in_error = 'error' in result
    except Exception:
        in_error = True
    if in_error:
        error_exit(
            'A connection could not be made with perforce. Check your settings and try again.',
            not config.automated)
예제 #8
0
def genproj_func(config: ProjectConfig, run_it):
    """ Generate project file """
    print_action('Generating Project Files')

    cmd_args = [
        '-ProjectFiles', '-project={}'.format(config.uproject_file_path),
        '-game', '-engine'
    ]
    if config.engine_minor_version <= 25:
        cmd_args.append('-VS{}'.format(
            get_visual_studio_version(config.get_suitable_vs_versions())))

    if launch(config.UE4UBTPath, cmd_args) != 0:
        error_exit('Failed to generate project files, see errors...',
                   not config.automated)

    if run_it:
        launch(os.path.join(config.uproject_dir_path,
                            config.uproject_name + '.sln'),
               separate_terminal=True,
               should_wait=False)
예제 #9
0
def tools(config: ProjectConfig, script):
    if not os.path.isfile(script):
        error_exit('No build script defined! Use the -s arg')

    with open(script, 'r') as fp:
        try:
            script_json = json.load(fp)
        except Exception as jsonError:
            error_exit('Build Script Syntax Error:\n{}'.format(jsonError))
            return
        if not config.load_configuration(script_json, ensure_engine=True):
            error_exit('Invalid Script file!')
예제 #10
0
            # First sync, so do a build
            if not do_project_build(['--error_pause_only']):
                sys.exit(1)
            else:
                build_checker.update_repo_rev_cache()
                build_checker.save_cache()


@tools.command()
@pass_config
def report_repo_status(config: ProjectConfig):
    print_action('Repo Status:')
    build_checker = ProjectBuildCheck(config)
    build_checker.check_and_print_repo_status()


# def main_test():
#     message = click.prompt('Type commit message')
#     messages = message.split('\\n')
#     git_cmd = ["git", "commit"]
#     for message in messages:
#         git_cmd.append('-m')
#         git_cmd.append('"- {}"'.format(message.strip()))
#     print(git_cmd)

if __name__ == "__main__":
    try:
        tools()
    except Exception as e:
        error_exit('{}'.format(e), False)
예제 #11
0
def build_script(engine, script, configuration, buildtype, build, platform,
                 clean, automated, buildexplicit):
    """
    The Main call for build script execution.
    :param engine: The desired engine path, absolute or relative.
    :param script: The Project Script which defines the projects paths, build steps, and extra information.
    :param configuration: Build configuration, e.g. Shipping
    :param buildtype: Which type of build are you trying to create? Editor OR Package?
    :param build: Which build steps to execute?
    :param platform: Which platform to build for?
    :param clean: Causes all actions to consider cleaning up their workspaces before executing their action.
    :param automated: Configures the builder to recognize this build as being done by continuous integration and should
                      not manipulate the system environment.
    :param buildexplicit: Should the build system only build what is requested? This prevents convienience cases like
                          the package build building the editor before trying to package. By setting this to true, it is
                          expected that the user has setup the proper state before building.
    """
    # Fixup for old build type 'Game'.
    if buildtype == 'Game':
        buildtype = 'Editor'

    global is_automated
    if automated:
        is_automated = automated

    # Ensure Visual Studio is installed
    if get_visual_studio_version() not in [2015, 2017]:
        print_error('Cannot run build, valid visual studio install not found!')
        return False

    if not os.path.isfile(script):
        error_exit('No build script defined! Use the -s arg', not is_automated)

    with open(script, 'r') as fp:
        try:
            script_json = json.load(fp)
        except Exception as jsonError:
            error_exit('Build Script Syntax Error:\n{}'.format(jsonError),
                       not is_automated)
            return

    config = ProjectConfig(configuration, platform, False, clean, automated)
    if not config.load_configuration(script_json, engine, buildexplicit):
        error_exit('Failed to load configuration. See errors above.',
                   not config.automated)

    print_title('Unreal Project Builder')

    if config.automated:
        click.secho('\nAutomated flag set!')

    # Ensure the engine exists and we can build
    if not buildexplicit:
        ensure_engine(config, engine)
    click.secho('\nProject File Path: {}\nEngine Path: {}'.format(
        config.uproject_dir_path, config.UE4EnginePath))

    # Ensure the unreal header tool exists. It is important for all Unreal projects
    if not buildexplicit:
        if not os.path.isfile(
                os.path.join(config.UE4EnginePath,
                             'Engine\\Binaries\\Win64\\UnrealHeaderTool.exe')):
            b = Build(config, build_name='UnrealHeaderTool')
            if not b.run():
                error_exit(b.error, not config.automated)

    # Build required engine tools
    if config.should_build_engine_tools and not buildexplicit:
        clean_revert = config.clean
        if buildtype == "Package":
            config.clean = False  # Don't clean if packaging, waste of time

        b = Build(config, build_names=config.build_engine_tools)
        if not b.run():
            error_exit(b.error, not config.automated)

        config.clean = clean_revert

    # If a specific set of steps if being requested, only build those
    if build != '':
        steps = Buildsteps(config, steps_name=build)
        if not steps.run():
            error_exit(steps.error, not config.automated)
    else:
        if buildtype == "Editor":
            if config.editor_running:
                error_exit(
                    'Cannot build the Editor while the editor is running!',
                    not config.automated)

            if 'game_editor_steps' in config.script:
                steps = Buildsteps(config, steps_name='game_editor_steps')
                if not steps.run():
                    error_exit(steps.error, not config.automated)
            elif 'editor_steps' in config.script:
                steps = Buildsteps(config, steps_name='editor_steps')
                if not steps.run():
                    error_exit(steps.error, not config.automated)
            else:
                b = Build(config,
                          build_name='{}Editor'.format(config.uproject_name))
                if not b.run():
                    error_exit(b.error, not config.automated)

        elif buildtype == "Package":
            # We need to build the editor before we can run any cook commands. This seems important for blueprints
            # probably because it runs the engine and expects all of the native class RTTI to be up-to-date to be able
            # to compile the blueprints. Usually you would be starting a package build from the editor, so it makes
            # sense. Explicit builds ignore this however.
            if not buildexplicit:
                b = Build(config,
                          build_name='{}Editor'.format(config.uproject_name))
                if not b.run():
                    error_exit(b.error, not config.automated)

            if 'package_steps' in config.script:
                steps = Buildsteps(config, steps_name='package_steps')
                if not steps.run():
                    error_exit(steps.error, not config.automated)
            else:
                package = Package(config)
                if not package.run():
                    error_exit(package.error, not config.automated)

    print_action('SUCCESS!')
    if not config.automated:
        click.pause()
예제 #12
0
def ensure_engine(config, engine_override):
    """
    Pre-work step of ensuring we have a valid engine and enough components exist to do work
    :param config: The project configuration (may not point to a valid engine yet)
    :param engine_override: The desired engine directory path to use
    """
    can_pull_engine = config.git_repo != '' and config.git_proj_branch != ''

    if config.UE4EnginePath == '':
        if not can_pull_engine and engine_override == '':
            error_exit(
                'Static engine placement required for non-git pulled engine. '
                'You can specify a path using the -e param, or specify git configuration.',
                not config.automated)

        if engine_override != '':
            config.setup_engine_paths(os.path.abspath(engine_override))
        elif not config.automated:
            result = click.confirm(
                'Would you like to specify the location of the engine install?',
                default=False)
            if result:
                result = click.prompt(
                    'Where would you like to install the engine?')
                if not os.path.exists(result):
                    try:
                        os.makedirs(result)
                    except Exception:
                        error_exit(
                            'Unable to create engine directory! Tried @ {}'.
                            format(result), not config.automated)
                config.setup_engine_paths(result)
            else:
                # Find an ideal location to put the engine
                if len(config.engine_path_name) == 0:
                    # Put the engine one directory down from the uproject
                    engine_path = os.path.abspath(
                        os.path.join(
                            config.uproject_dir_path,
                            '..\\UnrealEngine_{}'.format(
                                config.uproject_name)))
                else:
                    if os.path.isabs(config.engine_path_name):
                        engine_path = config.engine_path_name
                    else:
                        engine_path = os.path.normpath(
                            os.path.join(config.uproject_dir_path,
                                         config.engine_path_name))

                if not os.path.exists(engine_path):
                    try:
                        os.makedirs(engine_path)
                    except Exception:
                        error_exit(
                            'Unable to create engine directory! Tried @ {}'.
                            format(engine_path), not config.automated)
                config.setup_engine_paths(engine_path)
        else:
            error_exit(
                'No engine available for automated case! Either fill out git info or supply engine directory',
                not config.automated)
    elif config.UE4EnginePath != engine_override and engine_override != '':
        error_exit(
            'Specific engine path requested, but engine path for this project already exists?',
            not config.automated)

    # Before doing anything, make sure we have all build dependencies ready
    if can_pull_engine:
        git_action = Git(config)
        git_action.branch_name = config.git_proj_branch
        git_action.repo_name = config.git_repo
        git_action.output_folder = config.UE4EnginePath
        git_action.disable_strict_hostkey_check = True
        git_action.force_repull = False
        if not git_action.run():
            error_exit(git_action.error, not config.automated)

    if not config.setup_engine_paths(engine_override):
        error_exit('Could not setup valid engine paths!', not config.automated)

    # Register the engine (might do nothing if already registered)
    # If no key name, this is an un-keyed static engine.
    if config.UE4EngineKeyName != '' and not config.automated:
        register_project_engine(config, False)

    if not config.editor_running:
        print_action('Checking engine dependencies up-to-date')

        def add_dep_exclude(path_name, args):
            args.append('-exclude={}'.format(path_name))

        cmd_args = []
        if config.exclude_samples:
            for sample_pack in ['FeaturePacks', 'Samples']:
                add_dep_exclude(sample_pack, cmd_args)
        for extra_exclude in config.extra_dependency_excludes:
            add_dep_exclude(extra_exclude, cmd_args)

        if launch(config.UE4GitDependenciesPath, cmd_args) != 0:
            error_exit('Engine dependencies Failed to Sync!',
                       not config.automated)

        if not os.path.isfile(config.UE4UBTPath):
            # The unreal build tool does not exist, we need to build it first
            # We use the generate project files batch script because it ensures the build tool exists,
            # and builds it if not.
            print_action(
                "Build tool doesn't exist yet, generating project and building..."
            )
            if launch(config.UE4GenProjFilesPath, ['-2017']
                      if get_visual_studio_version() == 2017 else []) != 0:
                error_exit('Failed to build UnrealBuildTool.exe!',
                           not config.automated)
예제 #13
0
        cmd_args = []
        if config.exclude_samples:
            for sample_pack in ['FeaturePacks', 'Samples']:
                add_dep_exclude(sample_pack, cmd_args)
        for extra_exclude in config.extra_dependency_excludes:
            add_dep_exclude(extra_exclude, cmd_args)

        if launch(config.UE4GitDependenciesPath, cmd_args) != 0:
            error_exit('Engine dependencies Failed to Sync!',
                       not config.automated)

        if not os.path.isfile(config.UE4UBTPath):
            # The unreal build tool does not exist, we need to build it first
            # We use the generate project files batch script because it ensures the build tool exists,
            # and builds it if not.
            print_action(
                "Build tool doesn't exist yet, generating project and building..."
            )
            if launch(config.UE4GenProjFilesPath, ['-2017']
                      if get_visual_studio_version() == 2017 else []) != 0:
                error_exit('Failed to build UnrealBuildTool.exe!',
                           not config.automated)


if __name__ == "__main__":
    try:
        build_script()
    except Exception as e:
        error_exit('{}'.format(e), not is_automated)
예제 #14
0
def build_script(engine, script, configuration, buildtype, build, platform, clean):
    """
    The Main call for build script execution.
    :param engine: The desired engine path, absolute or relative.
    :param script: The Project Script which defines the projects paths, build steps, and extra information.
    :param configuration: Build configuration, e.g. Shipping
    :param buildtype: Which type of build are you trying to create? Editor OR Package?
    :param build: Which build steps to execute?
    :param platform: Which platform to build for?
    :param clean: Causes all actions to consider cleaning up their workspaces before executing their action.
    """
    # Fixup for old build type 'Game'.
    if buildtype == 'Game':
        buildtype = 'Editor'

    # Ensure Visual Studio is installed
    if get_visual_studio_version() not in [2015, 2017]:
        print_error('Cannot run build, valid visual studio install not found!')
        return False

    if not os.path.isfile(script):
        error_exit('No build script defined! Use the -s arg')

    with open(script, 'r') as fp:
        try:
            script_json = json.load(fp)
        except Exception as jsonError:
            error_exit('Build Script Syntax Error:\n{}'.format(jsonError))
            return

    config = ProjectConfig(configuration, platform, False, clean)
    if not config.load_configuration(script_json, engine, False):
        error_exit('Failed to load configuration. See errors above.')

    print_title('Unreal Project Builder')

    build_meta = BuildMeta('project_build_meta')
    if "meta" in config.script:
        build_meta.insert_meta(**config.script["meta"])

    # Ensure the engine exists and we can build
    ensure_engine(config, engine)
    click.secho('\nProject File Path: {}\nEngine Path: {}'.format(config.uproject_dir_path, config.UE4EnginePath))

    # Ensure the unreal header tool exists. It is important for all Unreal projects
    if not os.path.isfile(os.path.join(config.UE4EnginePath, 'Engine\\Binaries\\Win64\\UnrealHeaderTool.exe')):
        b = Build(config, build_name='UnrealHeaderTool')
        if not b.run():
            error_exit(b.error)

    # Build required engine tools
    clean_revert = config.clean
    if buildtype == "Package":
        config.clean = False  # Don't clean if packaging, waste of time
    for tool_name in config.build_engine_tools:
        b = Build(config, build_name=tool_name)
        if not b.run():
            error_exit(b.error)
    config.clean = clean_revert

    # If a specific set of steps if being requested, only build those
    if build != '':
        run_build_steps(config, build_meta, build, True)
    else:
        # Ensure engine is built
        if not config.editor_running:
            clean_revert = config.clean
            if buildtype == "Package":
                config.clean = False  # Don't clean if packaging, waste of time
            b = Build(config, build_name='UE4Editor')
            if not b.run():
                error_exit(b.error)
            config.clean = clean_revert
        else:
            print_warning('Skipping engine build because engine is running!')

        run_build_steps(config, build_meta, 'pre_build_steps')

        if buildtype == "Editor":
            if config.editor_running:
                print_warning('Cannot build the Editor while the editor is running!')
                click.pause()
                sys.exit(1)

            if 'game_editor_steps' in config.script:
                run_build_steps(config, build_meta, 'game_editor_steps')
            elif 'editor_steps' in config.script:
                run_build_steps(config, build_meta, 'editor_steps')
            else:
                b = Build(config, build_name='{}Editor'.format(config.uproject_name))
                if not b.run():
                    error_exit(b.error)

        elif buildtype == "Package":
            if 'package_steps' in config.script:
                run_build_steps(config, build_meta, 'package_steps')
            else:
                package = Package(config)
                if not package.run():
                    error_exit(package.error)

        run_build_steps(config, build_meta, 'post_build_steps')

    build_meta.save_meta()
    print_action('SUCCESS!')
    click.pause()
예제 #15
0
def run_build_steps(config: ProjectConfig, build_meta: BuildMeta, steps_name, complain_missing_step: bool=False):
    """
    Runs the build steps defined in a build script.
    A valid script json must be loaded in config!
    :param config: The configuration for this project
    :param build_meta: Build meta which might contain requirements for a build step
    :param steps_name: The steps to perform, defined in the script
    :param complain_missing_step: True if you would like the steps runner to complain about this step not existing.
    """
    if config.script is None:
        print_warning('Script json not loaded for run_build_steps!')
        return

    if steps_name in config.script:
        steps = config.script[steps_name]
        for step in steps:
            if "enabled" in step and step["enabled"] is False:
                continue

            print_action('Performing Undescribed step' if 'desc' not in step else step['desc'])

            # Get the step class
            step_module = importlib.import_module(step['action']['module'])
            class_name = step['action']['module'].split('.')[-1]
            action_class = getattr(step_module, class_name.title(), None)
            if action_class is None:
                print_warning('action class ({}) could not be found!'.format(class_name.title()))
                continue

            # Create kwargs of requested arguments
            kwargs = {}
            if 'meta' in step['action']:
                kwargs.update(build_meta.collect_meta(step['action']['meta']))

            if 'args' in step['action']:
                kwargs.update(step['action']['args'])

            # Run the action
            # We deep copy the configuration so it cannot be tampered with from inside the action.
            b = action_class(deepcopy(config), **kwargs)
            verify_error = b.verify()
            if verify_error != '':
                if "allow_failure" in step and step["allow_failure"] is True:
                    print_warning(verify_error)
                    print_warning('Verification of this action failed. Skipping because of allow_failure flag.')
                    return
                else:
                    error_exit(verify_error)

            if not b.run():
                if "allow_failure" in step and step["allow_failure"] is True:
                    print_warning(b.error)
                    print_warning('Running of this action failed. Skipping because of allow_failure flag.')
                    return
                else:
                    error_exit(b.error)

            # Do meta updates
            if 'meta_updates' in step['action']:
                for k, v in step['action']['meta_updates'].items():
                    meta_item = getattr(b, v, None)
                    if meta_item is not None:
                        setattr(build_meta, k, meta_item)
                build_meta.save_meta()
    elif complain_missing_step:
        print_warning('Could not find build step ({}) for run_build_steps!'.format(steps_name))
예제 #16
0
                if "allow_failure" in step and step["allow_failure"] is True:
                    print_warning(verify_error)
                    print_warning('Verification of this action failed. Skipping because of allow_failure flag.')
                    return
                else:
                    error_exit(verify_error)

            if not b.run():
                if "allow_failure" in step and step["allow_failure"] is True:
                    print_warning(b.error)
                    print_warning('Running of this action failed. Skipping because of allow_failure flag.')
                    return
                else:
                    error_exit(b.error)

            # Do meta updates
            if 'meta_updates' in step['action']:
                for k, v in step['action']['meta_updates'].items():
                    meta_item = getattr(b, v, None)
                    if meta_item is not None:
                        setattr(build_meta, k, meta_item)
                build_meta.save_meta()
    elif complain_missing_step:
        print_warning('Could not find build step ({}) for run_build_steps!'.format(steps_name))

if __name__ == "__main__":
    try:
        build_script()
    except Exception as e:
        error_exit('{}'.format(e))