J.U.S.T. Files
Justfile
- Justfile
Your Justfile
is the main file sourced by just that defines a just project. The primary purpose of the Justfile is to define the function caseify
.
- caseify
caseify
is a user defined function that parses and handle one call (J.U.S.T. will handle multiple calls by calling caseify multiple times for you, so you must not use a while loop and try to parse multiple calls). This should be done using a case
statement, or else the just help
parsing will not identify targets correctly. If you choose not to use a case
statement, comments in the form of # target) # Help text here
can still be utilized to satisfy just help
’s parsing.
caseify
should try to match the first argument ($1
) and keep count of the number of additional arguments used and stored the tally in extra_args
.
Example
#!/usr/bin/env bash
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then # If being sourced
set -euE
fi
source "${VSI_COMMON_DIR}/linux/just_env" "$(dirname "${BASH_SOURCE[0]}")/cyclops.env"
cd "${PROJECT_CWD}"
function caseify()
{
local just_arg=$1
shift 1
case ${just_arg} in
foo) #ls foo files
ls
;;
two) # Target with two arguments
echo $1 $2
extra_args=2
;;
# When a target starts with an _, it is hidden and is not included in
# tab complete. It remains hidden from help unless a help comment is
# added
_hidden)
echo Shhhhh
;;
*)
defaultify "${just_arg}" ${@+"${@}"}
;;
esac
}
if ! command -v justify &> /dev/null; then caseify ${@+"${@}"};fi
See also
just_functions.bsh source_environment_files
Sources environment
just_functions.bsh get_args
Parses a block of arguments
just_functions.bsh get_additional_args
Parses another block of arguments
just help
Help test
just JUSTFILE
Sets
Justfile
filename to load
- setup.env
The setup.env
is a necessary evil that must be sourced every time you start a terminal session. It mainly contains the relative location of the vsi_common
repo and adds just to the path using that. It should look like:
export JUST_SETUP_SCRIPT="$(basename "${BASH_SOURCE[0]}")"
source "$(dirname "${BASH_SOURCE[0]}")/external/vsi_common/env.bsh"
unset JUSTFILE
The first line is a place holder for a feature that has yet to be created
The third line fixes a bug when you are switching between projects and don’t want to accidentally use the wrong Justfile. If you are using a custom Justfile, this would be the place to set it.
The second line is the important line that sets up vsi_common on the path so that typing
just
works
Project Environment Files
A good Justfile
and just project should have no hard coded paths or values in it, but should work out of the box with their defaults. But if that’s the case, the defaults need to come from somewhere, and a way to override those values locally need to go somewhere. These values come from the project environment files.
- project.env
Your default settings go in your project settings env file. Unlike the Justfile
, you are encouraged to customize the name of this file. The name of your settings file is passed to just_env
in your Justfile
and is automatically stored in JUST_SETTINGS
. In the example above, you can see the name cyclops.env
was used.
# Required field
JUST_PROJECT_PREFIX=CYCLOPS
# Highly recommended field. Just uses this to detect changes in just version.
JUST_VERSION="0.2.2"
# Recommended field
if [ -z "${CYCLOPS_CWD+set}" ]; then
CYCLOPS_CWD="$(\cd "$(\dirname "${BASH_SOURCE[0]}")"; \pwd)"
fi
There are three fields you should always define in just file
JUST_PROJECT_PREFIX
- Designates that environment variables that start with this prefix are special to your project. Some of the advanced J.U.S.T. Plugins use this for automatic features, and just will refuse to work without this being set.JUST_VERSION
is recommended so that just can detect when yourJustfile
is behind in version, so you will get a warning to updated it before updating this version number.${JUST_PROJECT_PREFIX}_CWD
- Some of the advanced J.U.S.T. Plugins use this for automatic features, to know where the root of your source directory is.
After this, you are free to define any variables you like, provided they allow themselves to be overridden. A special feature of just is that all variables in this file are automatically exported for any command called by just. (Arrays are never exported because arrays simply cannot be exported to children processes). There is no need to ever says export CYCLOPS_HI_FILE
, since it is already exported. All you have to do is start setting variables to be overridable. The bash notation for variables vs arrays are different for this.
# How to set variables
: ${CYCLOPS_HI_FILE=${CYCLOPS_CWD}/hi.cpp}
: ${CYCLOPS_UID=$(id -u)}
# How to set arrays
set_array_default CYCLOPS_ARCH x86_64 i686 arm6
Both notations mean “If this variable is not set, then set it to this value. In the case of the variable:
The
=
expansion in bash means “If the variable name on the left is not set, then set the variable to the value on the right”.The
=
expansion, like all expansions in bash, is returned as a “command” or “argument” to be executed. Well we want to neither execute the value of the variable nor pass it to a command. So we use the:
character which is a shorthand for “true”. So while this is like calling true with an argument that is ignored, this is a short and concise way to “set a variable if it doesn’t exist.
Why was this notation not used for CYCLOPS_CWD
above? Because in bash, the right hand side is always executed, and this can result in a time penalty, especially on Windows. Since those lines are autogenerated, they use the long form of checking.
There is no clean or easy way to do the same for arrays in bash, so the function just_functions.bsh set_array_default
is provided to accomplish the same thing, and handle all the corner cases. In this case: “If CYCLOPS_ARCH
is unset, then it sets it to the 3 element array (x86_64 i686 arm6)
There is no simple solution for associative arrays (because they are not bash 3.2 compatible) or noncontiguous arrays (because there has never been a need for that).
An additional pattern that is commonly added to the end of a project setting file, is when there are special “Non-project specific variables that executables you use” need. For example:
PYTHONPATH
- Let’s say every call to python should have a special value forPYTHONPATH
, then it makes sense to add that to your settings file, but only if it should be every call to python.OMP_NUM_THREADS
- Let’s say you are running some multi-threaded code and needed to limit the number of threads, this too would make sense to add to the special end of settings sectionEtc… If the answers to “Is this a variable name I didn’t make up?” and “Does it make sense to set this for every program I’m going to call?” are yes, then it would be appropriate to put it here.
: ${CYCLOPS_THING1=15}
###############################################################################
# Non-CYCLOPS Settings
###############################################################################
: ${OMP_NUM_THREAD=8}
: ${TZ=/usr/share/zoneinfo/UTC}
: ${PYTHONPATH=${CYCLOPS_CWD}/python_code}
Some things you should never do in your settings file are:
Output on standard error or standard out (except for debugging). It will just pollute the screen.
Set variables without checking if they are set first, except for
JUST_PROJECT_PREFIX
andJUST_VERSION
Be fast. While you are allowed and encouraged to do anything you can do in a bash script, if you intend on taking the md5sum of a 100GB file, maybe you want to rethink when that gets done instead of doing it always (i.e. move that to a specific
Justfile
target).
- local.env
- local_post.env
If the project settings contain the default values, where do the the override values go? The answer is the local.env
file. If no local.env
file exists the first time you run just, an empty file is created for you. This is where you put the values for your “local install” of the the project. There is no need to use the same fancy notation (unless you want to make them overridable by exported variables from the terminal).
TZ=/usr/share/zoneinfo/EST
OMP_NUM_THREAD=2
The local.env
is loaded before the project settings env file is loaded, so it is impossible to use any values evaluated in it. This is usually not a problem, but in those uncommon cases, that is why there is a local_post.env
file. This file is used so rarely, that is not created for you by default, you’ll just need to create the empty file yourself.
CYCLOPS_HI_FILE="${CYCLOPS_CWD}/hi2.cpp"
Now local_post.env
is run after the project settings env file is loaded, so if you changed CYCLOPS_CWD
, any variable using its default value derived from CYCLOPS_CWD
(e.g. PYTHONPATH
) will not have a changed value. You will either need to set the value in the local.env
file, or re-set every variable that derived from it in the local_post.env
. Usually this is never an issue.
It is suggested to use the default names for the local env files, however they can be set to custom names using the JUST_LOCAL_SETTINGS and JUST_LOCAL_SETTINGS_POST variable. These will need to either be exported in setup.env
(or even somewhere like your .bashrc, depending on your use case) or set in Justfile
.
Note
The local.env
and local_post.env
should never be committed to a repository. They are part of your specific configuration. Like-wise you should not be editing the project default settings env file just to customize a project on your specific computer, that should always go in the local.env
/local_post.env
files.
Advanced setups
Chaining a multiple Justfiles
Chaining a multiple Justfiles (usually using a submodule)
The main function in just is Justfile caseify
, and there can only be one. The current solution to this is to make the main repo contain the Justfile caseify
function, and treat the submodule as a just plugin.
#!/usr/bin/env bash
source "${VSI_COMMON_DIR}/linux/just_env" "$(dirname "${BASH_SOURCE[0]}")"/'monocle.env'
# Make monocle's justfile a plugin if it is not the main Justfile
if [ "${JUSTFILE}" != "${BASH_SOURCE[0]}" ]; then
JUST_HELP_FILES+=("${BASH_SOURCE[0]}")
else
cd "${MONOCLE_CWD}"
# Allow monocle to be run as a non-plugin too
function caseify()
{
defaultify ${@+"${@}"}
}
fi
# Always add this to the list, because of how the caseify above works
JUST_DEFAULTIFY_FUNCTIONS+=(monocle_caseify)
# Main function
function monocle_caseify()
{
local just_arg=$1
shift 1
case ${just_arg} in
sometarget) # Do something
ls
;;
# ...
*)
plugin_not_found=1
;;
esac
return 0
}
As you can see, the only thing that differentiates this from a plugin is a special if statement that will define Justfile caseify
when it’s not already defined.