Integrate MSYS2 into Cmder
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.
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
3msys2.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: MSYS
、MINGW64
、MINGW32
, 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
3set 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:
- create
CMDER_ROOT
and inject Cmder-related paths intoPATH
- 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
22PS1='\[\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
25alias 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: