Files
UnrealEngine/Engine/Extras/ushell
2025-05-18 13:04:45 +08:00
..
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00
2025-05-18 13:04:45 +08:00

 ushell - A command line interface for the Unreal Engine

# Quick Start Guide

1. Create a shortcut to 'ushell.bat'
2. Set the start up directory to the location of a .uproject
3. Start ushell.
4. Run '.help' to see available commands.

The properties of the shortcut should look like this;

    - Target:   d:\ushell\ushell.bat
    - Start in: d:\perforce\branch\QAGame

Alternatively you can use the '--project=' command line argument;

    - Target:   d:\ushell\ushell.bat --project=d:\perforce\branch\QAGame
    - Start in: d:\my\favourite\directory

The `.project [path_to_uproject]` command can be used to change the session's
active project from within ushell.

# Mac and Linux

On POSIX-based platforms ushell works by establishing itself in the current
shell;

  source ushell.sh

The arguments and working directory rules in the Quick Start Guide above also
apply when sourcing ushell.sh. Ensuring a suitable version of Python is available
(and possibly a toolchain to build Pips) is left to the user to take care of.

# Commands

Perhaps the best way to understand some commands available in ushell and what
they can do is to list a few examples;

 1. .build editor
 2. .build game ps4
 3. .build program UnrealInsights shipping
 4. .cook game ps4
 5. .stage game ps4
 6. .run editor
 7. .run game ps4 --trace -- -ExecCmds="Zippy Bungle Rainbow"
 8. .run program UnrealInsights shipping
 9. .p4 cherrypick 1234567
10. .sln generate
11. .info

Each of ushell's commands accept a '--help' argument which will show
documentation for the command, details on how it is invoked, and descriptions
for the available options.

# Tab Completion And Command History

Anyone familiar with editing commands and recalling previous ones in Bash (i.e.
Readline) will feel at home at a ushell prompt.

There is extensive context-sensitive tab completion available for commands and
their arguments. Hitting Tab can help both to discover commands and their
arguments, and also aid in quick and convenient command entry. For example;

1. .<tab><tab>            : displays available commands
2. .b<tab>                : completes ".build "
3. .run <tab><tab>        : shows options for the .run commands first argument
4. .build editor --p<tab> : adds "--platform=" (further Tabs complete platforms)

Ushell maintains a history of previously run commands which persists from one
session to the next. Previous commands can be conveniently recalled in a few
ways. To step backwards through prior commands by prefix use PgUp;

1. .bu<pgup>                : Cycle back through commands that started with ".bu"
2. .run game switch<pgup>   : Iterate through previous runs on Switch.

A more thorough incremental history search is done with Ctrl-R. This displays a
prompt to enter a search string and will display the latest command with a match.
Further Ctrl-R hits will step backwards through commands that match the search
string (with Ctrl-S stepping forwards). History searching is case-sensitive.

# Integrating with UnrealGameSync

UGS supports additional tools deployable via Perforce via the UGS setting
`DeploymentSettings/ToolsDepotPath`. By adding a `Tools/ushell/ushell.ini` file
users can enable a ushell status panel link via `Options > Application
Settings`. An example `ushell.ini` follows;

```
[Settings]
Id=922EED87-E732-464C-92DC-5A8F7ED955E2
Name=ushell
Description=ushell
SafeWhenBusy=1
+StatusPanelLinks=(Label="ushell", FileName="$(COMSPEC)", Arguments="/c \"\"$(BranchDir)\\Engine\\Extras\\ushell\\ushell.bat\" --project=\"$(ProjectFile)\"\"", WorkingDir="$(ProjectDir)")
```

It is also possible to distribute a copy of ushell as a `ushell.zip` file
alongside `ushell.ini`. This can be useful to deploy ushell to users regardless
of which branch they may be working on.

`.ushell gather` is available to gather ushell from `Engine/Extras/ushell/` and
create a standalone deployment. This can be useful as a mechanism for managing
site-specific commands, or having a different ushell update schedule compared to
that for engine integrations.

# Scripting

There is modest support for scripting ushell with Batch scripts. This can be
useful for example to schedule overnight sync-builds. Here is a minimal example;

```
@echo off
cd /d d:\branch\myuproj
call d:\ushell\ushell.bat
.p4 sync --all
.build editor
.build client ps4
```

# Chaining Commands

It is also possible to chain commands entered on the command line with '&&';

  .build editor && .cook game ps4

The '&&' will only execute the subsequent command if the previous one succeeded.
Using a single '&' unconditionally runs the next command.

# Customising the Shell

If the host shell is the standard Windows command prompt then ushell will check
for and run `$USERPROFILE/.ushell/hooks/startup.bat` allowing the user to extend
the session. Here is an example script that adds a simple `.bcr` alias;

```
@echo off
doskey .bcr=.uat BuildCookRun -- $*
```

# Alternative Terminals

To use ushell in an alternative terminal such as Windows Terminal or VSCode's
integrated terminal ushell should be started as follows;

  cmd.exe /d/k d:\ushell\ushell.bat --project=d:\branch\myuproj\myuproj.uproject

This is required because by default ushell.bat detects if it was launched
explicitly through Explorer (or a shortcut) and if not the assumption is it is
running in a non-interactive scripting context (see `Scripting`)

# Alternative Host Shells

A lot of the above assumes that cmd.exe is the host shell. Other shells are also
supported; Bash or Zsh on POSIX-based platforms, and PowerShell. There are
scripts in ushell's root folder for using these alternative shells.

## PowerShell

PowerShell integration works by importing ushell as a module. Powershell wants
modules to be contained within a directory matching the name of the module, so the
'powerushell' module is contained in the path ushell\powerushell\powerushell.psm1.

Set your `PSModulePath` to contain the ushell directory (this can be done in your
`$PROFILE` file).

```
$env:PSModulePath = "$($env:PSModulePath);c:\path\to\ushell\"
```

Then you can import the module, either in your profile or when you want to enter a
ushell session for a particular terminal session.

```
Import-Module powerushell
```

If you use a PowerShell prompt enhancer like oh-my-posh you can extract
environment variables from ushell to populate your prompt/window title/etc. The
ushell-related environment variables can be list with `dir env:USHELL*`.

```
function Set-MyPoshContext {
    if ($Global:WhatIfPreference) {
        $env:POSH_WHATIF = "What If"
    }
    else {
        $env:POSH_WHATIF = ""
    }
    if ( $null -ne (Get-Module ushell)) {
        Update-UShellEnvVars | Out-Null
    }
}
New-Alias -Name 'Set-PoshContext' -Value 'Set-MyPoshContext' -Scope Global -Force
```

# Extending ushell

## Case Study

A ushell instance is consists of "flow" which provides the framework for building
a shell and commands for it, and channels, where a channel is a group of commands
and tools. Flow enumerates channels by looking in the `channels/` folder for a
well-known file that describes the channel. On disk a channel may look like;

```
channels/
  mychannel/
    describe.flow.py
    cmds/
      mycmd.py
```

The `describe.flow.py` is how ushell detects the subfolder as a channel. The file
describes what commands the channel adds and how they are invoked. There is also
an API for describing tools and how to fetch them, and a basic plugin mechanism.
Here is a basic example channel with a single command;

```
import flow.describe

# Describe the channel's commands. source() states where the source .py is and
# the class that implements the command. The invoke() method sets how to the
# command should be invoked from the shell - "d:\>.mycmd zippy" in this example
my_cmd = flow.describe.Command()
my_cmd.source("cmds/mycmd.py", "MyCmdClass")
my_cmd.invoke("mycmd", "zippy")

# Describe the channel. The pip() method can be used to install pip from the
# PyPi repository. version() can be used to invalidate a channel when updates
# are pulled. The parent() forms channels into a tree and informs the order of
# inheritance when overriding commands.
channel = flow.describe.Channel()
channel.parent("unreal.core")
channel.version("0")
#channel.pip(...)
```

A command is implemented by deriving a class from `flow.cmd.Cmd` and using a
docstring and `flow.cmd.Arg/Opt` classes to set a description and specify the
commands arguments. Given the example channel above the `mycmd.py` might look
as follows;

```
import flow.cmd

class MyCmdClass(flow.cmd.Cmd):
    """ This is the description of my command shown when --help is given """

    # Describe the commands paramsters with Arg and Opt objects
    argone = flow.cmd.Arg(str,   "A positional argument that must be given")
    argtwo = flow.cmd.Arg("two", "A positional argument with a default value")
    option = flow.cmd.Opt(False, "An boolean optional argument; --option")
    param  = flow.cmd.Opt(5,     "An optional argument that takes a parameter; --param=6")

    # Add completion for arguments with a complete_[argname] method. Completion
    # methods return something iterable (thus they can be generator functions).
    def complete_argone(self, prefix):
        return ("zippy", "bungle", "george")

    # The entry point of the command. Arguments are prepopulated in self.args
    def main(self):
        if self.args.option:
            print(self.args.argument)
```

For working with Unreal branches there is an `unrealcmd` module that specialises
a `flow.cmd.Cmd` object. The most useful method is `get_unreal_context()` which
gives access to the branch, project, targets, and more (details can be found by
reading `channels/unreal/core/pylib/unreal/_context.py`). An example;

```
import unrealcmd

class MyUnrealCmdClass(unrealcmd.Cmd):
    """ This command works with an Unreal branch and/or project """
    argument = unrealcmd.Arg(int,   "...")
    optional = unrealcmd.Opt(False, "...")

    ue_context = self.get_unreal_context()
    if project := ue_context.get_project():
        print("Project name", project.get_name())
```

## Miscellaneous

It possible to inject into existing commands by matching the words used to invoke
them. So if the case study example was changed to `my_cmd.invoke("build", "editor")`
then executing `.build editor` would in fact call `MyCmdClass.main()`. Calling
`super().main()` would in turn run the proper ushell command to build the editor.

There are a few more details about channels not covered thus far. Briefly; a
channel's `pylib` folder is added to PYTHON_PATH so channels can define modules
(handy for sharing between commands). Channels can participate in the boot
process and prompt building - examples can be found in channels with boot and
prompt commands.

Channel names are derived from their file system location; `channels/geoffrey`
gets the name `geoffrey`, and `channels/mr/hayes` becomes `mr.hayes".

## Setting Yourself up to Extend ushell

Currently the best way to add channels is to sync create a clientspec with the
depot path `//depot/usr/martin.ridgers/ushell` in view along with your own
channel mapped into the `channel/X` folder.

Extending the UGS-deployed ushell is in essence impossible at this time for two
reasons; 1) the deployment is overwritten by UGS when a new version becomes
available, and 2) flow only looks in the channels/ sub-folder of where it is
deployed. _This is going to change_ so that users can add channels in a
well-known location that any distribution of ushell can find. To. Do.

# Contact

martin.ridgers@epicgames.com



vim: tw=80 fo=wnt ft=markdown nosi spell