Our team has a mix of developers on both Windows and Linux, and a shared code base that has to be able to compile on both, in the Microchip MPLAB X IDE v6.20, for PIC32 microcontrollers in our case.
I see in the project properties (right-click the project name --> Properties) there is an option for adding a pre-build step, here:
This could be used for auto-generating C code .h header files, for instance. How can we get a common pre-build script here to run on both Windows and Linux in the MPLAB X IDE?
I'm having a lot of challenges because the bash commands available in the MPLAB X IDE are inconsistent and have missing options and commands.
After much effort, I figured it out. One very functional and customizatable way to do it is to use a common .sh bash script which will run on both Linux and Windows. This bash script can then do whatever pre-build steps you want. It could call other executables, other bash scripts, Python scripts, or just do the work directly.
preBuildSteps.sh
Bash script that runs as part of the pre-build process in the MPLAB X IDE on both Windows and LinuxAdd this line into that "Execute this line before build"
box (and be sure to check the box), on both Windows and Linux:
"C:\Program Files\Git\git-bash.exe" ../preBuildSteps.sh && echo "Pre-build output logged to 'autogenerated/logs/'"
This assumes that:
"C:\Program Files\Git\git-bash.exe"
.preBuildSteps.sh
one level up from the *.X
MPLAB X project directory (see the full project structure/file tree further below in the answer). So, relative to the *.X
project directory, it is located at ../preBuildSteps.sh
.autogenerated/logs/
in the root of your project.On Windows, install Git for Windows, which contains the Git Bash terminal. I recommend you follow my instructions here: Installing Git For Windows.
On Linux, create an executable symlink to bash
, with a symlink filename of C:\Program Files\Git\git-bash.exe
(this is a legal filename in Linux), and place it into your ~/bin
directory, as follows:
# Ensure this directory exists
mkdir -p ~/bin
# Now create the symlink
ln -si $(which bash) ~/bin/"C:\Program Files\Git\git-bash.exe"
Now, ensure that ~/bin
is in your PATH. If on Linux Ubuntu, the following should already be in the bottom of your ~/.profile
file:
# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi
If it's not, then add that to the bottom of your ~/.profile
file (preferred) or ~/.bashrc
file and then re-source (import) it by running:
. ~/.profile
. ~/.bashrc
Now, you should be able to run 'C:\Program Files\Git\git-bash.exe'
as an executable alias to bash
from your Linux terminal. Test it by running the following, and you should see the exact same output from each:
# Check your Linux bash version
bash --version
# Also check the exact same Linux bash version via the symlink
# we just created above
'C:\Program Files\Git\git-bash.exe' --version
Now, create a bash script called preBuildSteps.sh
in the root of your project, and place your pre-build steps into it. Here is a very thorough example of what it might look like:
myProject/preBuildSteps.sh
:
#!/usr/bin/env bash
# This script contains pre-build steps to run at the start of the build in
# MPLAB X IDE.
# Get paths. See my answer: https://stackoverflow.com/a/60157372/4561887
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
LOG_DIR="$SCRIPT_DIRECTORY/autogenerated/logs"
LOG_FILE="$LOG_DIR/$SCRIPT_FILENAME.log"
mkdir -p "$LOG_DIR"
# Change to the script's directory so all relative paths work correctly.
# From this point on, all paths below can be relative to the script's directory.
cd "$SCRIPT_DIRECTORY"
# Function to print a separator in the terminal between commands.
print_separator() {
printf "\n====================\n\n"
}
# start of output log; see: https://serverfault.com/a/103509/357116
((
echo -e "\nRunning $SCRIPT_FILENAME..."
echo "Logging output to \"$LOG_FILE\"."
print_separator
# ------------------------- START OF PRE-BUILD STEPS ---------------------
# Run these prebuild scripts you may have
# Autogenerate at header file at:
# 1. autogenerated/autogenerated/MyAutogeneratedHeader1.h
# 2. autogenerated/autogenerated/MyAutogeneratedHeader2.h
# 3. autogenerated/autogenerated/MyAutogeneratedHeader3.h
./autogenerated/scripts/make_MyAutogeneratedHeader1.h.sh; print_separator
./autogenerated/scripts/make_MyAutogeneratedHeader2.h.sh; print_separator
./autogenerated/scripts/make_MyAutogeneratedHeader3.h.sh; print_separator
# Now handle running a **Windows** binary executable on Linux, by running it
# with `wine`.
# See my answer: https://stackoverflow.com/a/78480875/4561887
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
# OS is Linux, so use wine to run the Windows executable.
wine ./autogenerated/bin/myWindowsExecutable.exe arg1 arg2 arg3
elif [[ "$OSTYPE" == "msys" ]]; then
# OS is Windows (Git Bash), so run the Windows executable directly.
./autogenerated/bin/myWindowsExecutable.exe arg1 arg2 arg3
fi
print_separator
# ------------------------- END OF PRE-BUILD STEPS -----------------------
# end of output log
) 2>&1) | tee "$LOG_FILE"
Lastly, you should ignore your autogenerated files and logs via your .gitignore
file. Assuming you have this project structure (as shown by tree -a
):
myProject
├── autogenerated
│ ├── autogenerated
│ │ ├── MyAutogeneratedHeader1.h
│ │ ├── MyAutogeneratedHeader2.h
│ │ └── MyAutogeneratedHeader3.h
│ ├── bin
│ │ └── myWindowsExecutable.exe
│ ├── .gitignore
│ ├── logs
│ │ └── preBuildSteps.sh.log
│ ├── README.md
│ └── scripts
│ ├── make_MyAutogeneratedHeader1.h.sh
│ ├── make_MyAutogeneratedHeader1.h.sh
│ └── make_MyAutogeneratedHeader1.h.sh
├── preBuildSteps.sh
├── myProject.X
│ └── ...MPLAB X project configuration files and build output...
├── ...other project files...
...then here is an example of what your myProject/autogenerated/.gitignore
file shown above might look like to ignore all autogenerated files and logs, except for a README.md
file in each directory, if one exists:
myProject/autogenerated/.gitignore
:
# Ignore all files herein; see: https://stackoverflow.com/a/67551691/4561887
/autogenerated/*
# Except this file if it exists
!/autogenerated/README.md
# Ignore all files herein
/logs/*
# Except this file if it exists
!/logs/README.md
That's it! Now, when you build your project in MPLAB X, it will run the prebuild command of:
"C:\Program Files\Git\git-bash.exe" ../preBuildSteps.sh && echo "Pre-build output logged to 'autogenerated/logs/'"
On Windows, this opens a Git Bash terminal via "C:\Program Files\Git\git-bash.exe"
, and then runs your ../preBuildSteps.sh
Bash script within it. At the end, it prints Pre-build output logged to 'autogenerated/logs/'
so that you can see that inside your MPLAB X IDE build output window. This is helpful because the Git Bash popup window terminal will have just closed and you'll have missed all the output it printed, so you need a way to review the output in the log file.
On Linux, you have a magical symlink to bash
, in your PATH at ~/bin/"C:\Program Files\Git\git-bash.exe"
, so it simply runs the ../preBuildSteps.sh
Bash script directly inside of a new bash
sub-shell, and then of course also prints Pre-build output logged to 'autogenerated/logs/'
at the end.
The preBuildSteps.sh
script does whatever you want it to do. In my example above, I have it call other scripts to autogenerate some C header files, and I have it handle a unique case where it needs to run a Windows executable on Linux through wine
. I make it log all output to autogenerated/logs/preBuildSteps.sh.log
, which gets ignored by your .gitignore
file shown in the project tree above.
An alternative to creating the bash symlink on Linux via ln -si $(which bash) ~/bin/"C:\Program Files\Git\git-bash.exe"
is to create a bash wrapper script instead. Such a script would still be located at path ~/bin/"C:\Program Files\Git\git-bash.exe"
, but it would contain the following contents:
#!/usr/bin/env bash
# This is a wrapper to simulate Windows's Git Bash terminal in Linux
bash "$@"
It could be created by running these commands:
# Create the file (copy and paste this whole chunk into your terminal all
# at once, from here to the `echo` line below)
file_contents=$(cat << 'EOF'
#!/usr/bin/env bash
# This is a wrapper to simulate Windows's Git Bash terminal in Linux
bash "$@"
EOF
)
echo "$file_contents" > ~/bin/"C:\Program Files\Git\git-bash.exe"
# Make the file executable
chmod +x ~/bin/"C:\Program Files\Git\git-bash.exe"
The end result would be the exact same as using the symlink to bash
, but the symlink is easier. The wrapper script would be better if you need to have it do additional things before running bash.
../preBuildSteps.sh
scriptIf you have multiple build configurations in MPLAB X, rather than creating a separate ../preBuildSteps.sh
script for each one, you can simply pass in the build configuration name as an argument to the script, like this, for example, for myBuildConfiguration1
:
"C:\Program Files\Git\git-bash.exe" ../preBuildSteps.sh --myBuildConfiguration1 && echo "Pre-build output logged to 'autogenerated/logs/'"
...and then have the script do different things based on the build configuration name. For example, add this to your ../preBuildSteps.sh
script:
# ------------------------- START OF PRE-BUILD STEPS ---------------------
# Do common build configuration stuff here
# ...
# Do build configuration-specific stuff here
if [[ "$1" == "--myBuildConfiguration1" ]]; then
# Do unique stuff for Build Configuration 1
# ...
elif [[ "$1" == "--myBuildConfiguration2" ]]; then
# Do unique stuff for Build Configuration 2
# ...
elif [[ "$1" == "--myBuildConfiguration3" ]]; then
# Do unique stuff for Build Configuration 3
# ...
fi
# ------------------------- END OF PRE-BUILD STEPS -----------------------
/
(forward slash) and \0
(null char, or binary zero). See:
C:\Program Files\Git\git-bash.exe
python3
(like in Linux), and to run python scripts as ./myProgram.py
(like in Linux) while using Linux-style hash-bangs such as #!/usr/bin/env python3
at the top of your Windows and Linux Python scripts: Python not working in the command line of git bash