Recently I’m reading a Chinese book named “A Programmer Prepares“ and I need a Linux environment, but I do like Macrohard Microsoft stuffs and Cmder is really awesome.
So in order to avoid the not-so-beautiful bash, here comes this post.

Update 2018.06.09

Since version 1.3.5, Cmder removes CMDER_START and switch startup directory by using option /dir in CmderLauncher.cpp so the trick below fails now.
After a more dedicated investigation on Conemu, I’d like to write more reasonable instructions of the integration here, if you are new to this article and not in a hurry, I recommend you read this update section after finishing the whole article.

In fact, many problems can be solved by just setting several environment variables before starting your bash in MSYS2.
set "MSYSTEM=MSYS"& set "MSYS2_PATH_TYPE=inherit"& set "CHERE_INVOKING=1"& cmd /c "%MSYS2_ROOT%\usr\bin\bash -login -i" -new_console:n
Here I will talk about three variables (Actually, I originally found them from msys2_shell.cmd).

  • set "MSYSTEM=MSYS"
    Yes! This ‘set’ operation is always the most important part, as it determines many internal MSYS2 configuration and if you mismatch the environment you WILL mess up everything inside MSYS2. So check thrice before starting your MSYS2 journey.
  • set "MSYS2_PATH_TYPE=inherit"
    This ‘set’ is just for guys who want to keep from the default PATH triming and retrieve the whole Windows system PATH. You can still customize PATH by coding if you like.
  • set "CHERE_INVOKING=1"
    In Cygwin if an environment variable CHERE_INVOKING is not defined, Cygwin will ‘cd’ your startup directory to HOME, set it to 1 will prevent it. Seems that MSYS2 borrows this trick so we just use it. It’s also worth noting that without CMDER_START you can still get your shell start from ConEmuWorkDir.
    See more details at here and here
  • -new_console:n
    Sometimes you may encounter “ConEmuC: Root process was alive less than 10 sec, ExitCode=0”. Just use the switch option n to suppress it.

Which one? Cygwin, MinGW+MSYS, MinGW-w64, or MSYS2?

Before starting the integration, let’s take a minute to discuss which project can deliver a best Unix-like development environment on Windows platform and… Yes! You have known the answer from the title.
MSYS2 is a project based on modern Cygwin (POSIX compatibility layer), the core of which is an independent of MSYS. the Integration of MinGW-w64 makes MSYS2 more powerful when interoperating with native Windows software. And the most important, the “Git for Windows 2.x” in Cmder full-version exactly bases on MSYS2. Considering that the original MSYS2 is delivered with Arch Linux’s Pacman, so why not directly use MSYS2? With Pacman, git and gcc toolchain can be managed in a automatic way and we can kiss those installers goodbye!

Besides, It’s worth noting something about MinGW, MSYS and MinGW-w64. When I started to investigate these projects, firstly I was navigated to the official site of GCC and found a MinGW installer. The installer integrated MSYS, which resulted in a redundant copy of GNU Coreutiles in my OS (One from the new installed MSYS, and the other from my old Git for Windows). Luckily by the further investigation I found the alternative - MinGW-w64. The birth of MinGW-w64 is due to MinGW’s lack of 64-bit support and other conflicts between the authors of these two projects, but it doesn’t mean MinGW-w64 only targets 64-bit platforms, it does support both 32-bit and 64-bit. As the MSYS2 project‘s goal is to keep track with the modern Cygwin project while the original MSYS project can’t, I think it’s obvious why MSYS2 has chosen MinGW-w64.

How to launch MSYS2 in Cmder?

Firstly, let’s see how to launch MSYS2 normally.
At the root folder of MSYS2 there’s a key executable msys2_shell.cmd, and the other three .exe files are wrappers of msys2_shell.cmd with different command line argument:

1
2
3
msys2.exe   --> msys2_shell.cmd -msys
mingw64.exe --> msys2_shell.cmd -mingw64
mingw32.exe --> msys2_shell.cmd -mingw32

The most critical part of msys2_shell.cmd is to set environment variable MSYSTEM depending on the argument, and this variable directly affects the whole succeeding source process, even the logic of some executable files beneath /usr/bin. MSYSTEM has three possible values: MSYSMINGW64MINGW32, they correspond respectively to three subsystems. The MSYS subsystem provides an emulated mostly-POSIX-compliant environment, the msys-2.0.dll of which is the counterpart of runtime library cygwin1.dll in Cygwin, whereas the MINGW64&MINGW32 subsystems can be considered as MinGW-w64 environment, each of them respectively provides 64-bit (compiled by mingw-w64-x86_64-toolchain) and 32-bit (compiled by mingw-w64-i686-toolchain) native Windows programs.

Because Cmder is built on ConEmu with some enhancements, the problem of integrating MSYS2 becomes how to add a new task in ConEmu. msys2_shell.cmd has logic about launching different terminal emulator (ConEmu, Mintty, ConsoleZ) by arguments user passes in, luckily the logic branch for ConEmu is not so special, bash --login -i is its core command, so we can add three new tasks under the Settings > Startup > Task tab in ConEmu and fill the “Command” field with the following lines respectively:

1
2
3
set MSYSTEM=MSYS & cmd /c "%MSYS2_ROOT%\usr\bin\bash --login -i" -new_console:d:%USERPROFILE%
set MSYSTEM=MINGW64 & cmd /c "%MSYS2_ROOT%\usr\bin\bash --login -i" -new_console:d:%USERPROFILE%
set MSYSTEM=MINGW32 & cmd /c "%MSYS2_ROOT%\usr\bin\bash --login -i" -new_console:d:%USERPROFILE%

Each line will launch the corresponding subsystem. The %MSYS2_ROOT% refers to the root path of MSYS2 in Windows, you can add it into Windows environment variables or you can ignore it and just put an absolute path there. Do not forget to give every task a name and set one of them as default task.

How to make “Cmder Here” context menu shortcut work as normal

The GitHub main page of Cmder project has an instruction that executing .\cmder.exe /REGISTER ALL under the directory of Cmder.exe will create a shortcut into context menu. In fact launching Cmder by this way will just cause an environment variable CMDER_START to be added into current session, the actual cd action is done by the following lines in the %CMDER_ROOT%/vendor/init.bat:

1
2
3
4
5
:: This is either a env variable set by the user or the result of
:: cmder.exe setting this variable due to a commandline argument or a "cmder here"
if defined CMDER_START (
cd /d "%CMDER_START%"
)

Cmder uses this init.bat file to initialize everything and enhance the user experience for native Windows terminal cmd.exe (You can check other preset tasks to confirm), and as such, it will not be executed by our new created tasks. So the next step is to move the logic to the place where initializes the bash. Add the following lines into .bashrc (located in the home directory of MSYS2) to repair the broken “Cmder Here” shortcut:

1
2
3
4
5
# For "Cmder Here"
if [[ -n ${CMDER_START} ]]; then
echo "Changing into directory: ${CMDER_START}"
cd "${CMDER_START}"
fi

Notice that the null check ensure double-clicking Cmder.exe or clicking the shortcut from taskbar can also function well (In this case the CMDER_START is not set). By this way we can also avoid adding a /Start launching argument to the taskbar shortcut. However, there still exist a flaw. Somehow a redundant double quote will appear at the end of the path contained by CMDER_START when the cotext menu shortcut is executed from the root of a drive. It’s likely to be a issue of Cmder itself so we can temporarily ignore it before new version of Cmder is released. But anyway, you can add some more commands to remove the annoying double quote manually.

Injection from the Cmder side

Cmder create a cmder_exinit file under %CMDER_ROOT%/vendor/ and after adding a suitable suffix it can be put under /etc/profile.d/ to allow current shell “source” it after launch. the main task of this shell script is:

  1. create CMDER_ROOT and inject Cmder-related paths into PATH
  2. source all files under %CMDER_ROOT%/config/profile.d/ and %CMDER_ROOT%/config/user-profile.sh

Then all executable files can be reached in %CMDER_ROOT%/bin/.

In previous section we have added some custom code into .bashrc, but if following the Cmder official’s “portable” concept we should have put code into user-profile.sh to ensure all custom stuff can be taken away when migrating the whole Cmder directory to another machine. As for me, I think it’s just personal perference, each place is okay for me.

Another file needed to be injected into /etc/profile.d/ is git-prompt.sh, which is provided by Git for Windows project. It can bring git branch display and git command completion to bash. Its location is generally at %CMDER_ROOT%/vendor/git-for-windows/etc/profile.d/ and if you are using Cmder full-version it should not be so hard to find. For Cmder-mini user, it can still be download from the GitHub repo of Git for Windows. It’s worth noting that this git-prompt.sh is not the real one. Actual git-prompt.sh and git-completion.bash are located in your git package installed by using Pacman. This one is mainly used for setting PS1 and sourcing two files mentioned above. Here’s the main code segment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS1='\[\033]0;$MSYSTEM:${PWD//[^[:ascii:]]/?}\007\]' # set window title
PS1="$PS1"'\[\033[32m\]' # change to green
PS1="$PS1"'\u@\h ' # user@host<space>
PS1="$PS1"'\[\033[33m\]' # change to brownish yellow
PS1="$PS1"'\w' # current working directory
if test -z "$WINELOADERNOEXEC"
then
GIT_EXEC_PATH="$(git --exec-path 2>/dev/null)"
COMPLETION_PATH="${GIT_EXEC_PATH%/libexec/git-core}"
COMPLETION_PATH="${COMPLETION_PATH%/lib/git-core}"
COMPLETION_PATH="$COMPLETION_PATH/share/git/completion"
if test -f "$COMPLETION_PATH/git-prompt.sh"
then
. "$COMPLETION_PATH/git-completion.bash"
. "$COMPLETION_PATH/git-prompt.sh"
PS1="$PS1"'\[\033[36m\]' # change color to cyan
PS1="$PS1"'`__git_ps1`' # bash function
fi
fi
PS1="$PS1"'\[\033[0m\]' # change color
PS1="$PS1"'\n' # new line
PS1="$PS1"'λ ' # prompt: always λ

Customize alias and PATH

Cmder itself provide a set of alias for cmder.exe (can be checked at %CMDER_ROOT%/config/user-aliases.cmd) and yes! again it’s just for cmder.exe. So again we should move them into .bashrc or user-profile.sh. Notice that correction is needed due to the different grammar (path format and variable reference) between Window and Linux. A special case is that when a path is a combination of Windows environment variable and other strings, MSYS2 cannot directly handle it and we should manually convert the format. The code is list below (for example, we want to use a external Node.js environment):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
alias ll='ls -alh --time-style=long-iso --show-control-chars -F --color $*'
alias e.='explorer .'
alias showpath=" tr ':' '\n' <<< \"\$PATH\" "

# Include Extra Paths
ExtraPaths=(
"${NODE_PATH}"
"${APPDATA}\npm"
)
for currPath in ${ExtraPaths[@]}
do
# Convert path from Windows to Unix format
if [ "$currPath" != "" ] ; then
case "$currPath" in *\\*) currPath="$(cygpath -u "$currPath")";; esac
fi
if [ "$currPath" != "" ] ; then
# Remove any trailing '/'
currPath=$(echo $currPath | sed 's:/*$::')

echo "Adding extra path \"${currPath}\"."
PATH=${currPath}:${PATH}
fi
export PATH
done
unset ExtraPaths

And we can get: