
Generate an HTCondor executable shell script for an R job
Source:R/htc-gen-executable.R
htc_gen_executable.Rdhtc_gen_executable() writes a ready-to-use bash script (.sh) that
HTCondor runs inside the container for each job. The script changes to
HTCondor's writable scratch directory, creates an output folder, runs
the R script via Rscript, and compresses the results into a tarball
for transfer back to the submit node. The tarball is built whether or
not the R script succeeds (see the section on failed jobs below).
Usage
htc_gen_executable(
output_file = NULL,
r_script = NULL,
data_files = NULL,
results_folder = NULL,
home_dir = "/home",
mode = "single",
set_executable = TRUE,
verbose = FALSE,
comments = FALSE,
output = ".",
config = NULL,
path = "."
)Arguments
- output_file
A character string or
NULL. Name of the shell script to write. Must end in".sh". WhenNULL(the default), resolves to theexecutable_filerecorded in the submission state by a previoushtc_gen_submit()call (S-I3), so the name only has to be typed once regardless of which of the two generators runs first. Falls back to"job.sh"if neither an explicit value nor a submission-state value is available. If the resolved value disagrees with anexecutable_filealready in the submission state, warns rather than silently preferring one over the other.- r_script
A character string. Name of the R script that HTCondor will run, e.g.
"analysis.R". Must be supplied explicitly – there is no default. If you usedtoolero::create_qmd(use_purl = TRUE), the script is the.Rfile produced bypurl.Rafter rendering. The script itself is not baked into the container image – it travels to the execute node as an uploaded job input file (seetransfer_input_filesinhtc_gen_submit()), so editing it only requires re-uploading and resubmitting, never a container rebuild. Because HTCondor's file transfer does not preserve subdirectories, only the file's basename is used inside the script; if you passr_script = "R/analysis.R", make sure"analysis.R"(not theR/path) is what actually lands ininput_filesforhtc_gen_submit().- data_files
A character vector or
NULL. Paths to data files baked into the container that should be passed to the R script as positional arguments. These are converted to absolute paths inside the container (e.g."data-raw/sample.csv"becomes"/home/data-raw/sample.csv"). The R script receives them viacommandArgs(trailingOnly = TRUE). Defaults toNULL.- results_folder
A character string or
NULL. Name of the folder created in the scratch directory to hold job outputs before compression. WhenNULL(the default), resolves toconfig$project$conventions$output_dirwhenconfigis supplied (S-G5), falling back to"output", the output folder used across the toolero family, when neither is available. Note that only this folder is created: if your analysis writes tooutput/figures/, the R script must create that subfolder itself, whichtoolero::save_output()does and a bareggsave()does not.- home_dir
A character string. The working directory inside the container where baked-in
data_fileslive. Used to construct absolute paths for data file arguments only – it has no effect on wherer_scriptis read from, since the R script is not baked into the image. Must match thehome_dirused incontainr::generate_dockerfile()fordata_files. Defaults to"/home".- mode
A character string. Execution mode.
"single"(the default) runs the R script with only the data file arguments (if any), producing a tarball named after the script alone."multiple"also passes the subset filename as the first positional argument via${1}, producing a per-job tarball that adds the subset's own stem, soanalysis.Roveradelie.csvgivesanalysis-adelie-results.tar.gz. Must match themodeused inhtc_gen_submit(), which has to declare the same name intransfer_output_files.- set_executable
Logical. If
TRUE, sets executable permissions on the generated script viaSys.chmod()so the file is ready to copy to the CHTC submit node without any additional steps. Defaults toTRUE. Set toFALSEif you prefer to manage permissions manually, in which case you must runchmod +xon the script before submitting your job.- verbose
Logical. If
TRUE, prints progress messages as each section of the script is written. Defaults toFALSE.- comments
Logical. If
TRUE, annotates each section with an explanatory comment describing what the line does. Useful for researchers learning the HTCondor executable script conventions. Defaults toFALSE.- output
A character string. Directory where the shell script will be written. Defaults to
"."(current working directory).- config
A named list as returned by
htc_config(), orNULL(the default). When supplied with aprojectelement (viahtc_config(project_config = )),config$project$conventions$output_diris used to defaultresults_folder(S-G5). Not required – everything here can still be passed explicitly.- path
A character string. Directory where the submission state (
htc-manifest.yml) is read from and written to. Defaults to"."(the current working directory), matching the default used byhtc_upload(),htc_submit(), andhtc_download(). This is independent ofoutput: if you write generated files to a subfolder withoutput, pass the samepathexplicitly to every function in the pipeline so they all find the same submission state.
Value
Called for its side effects. Writes a bash script to
file.path(output, output_file) and sets executable permissions when
set_executable = TRUE. Returns invisible(NULL).
How file paths work inside the container
The generated script draws on two different sources for its inputs, and reads each one a different way:
The R script – r_script is not baked into the container image.
It travels to the execute node as an uploaded job input file, listed in
transfer_input_files by htc_gen_submit(). HTCondor's file transfer
does not preserve subdirectories, so whatever you pass as r_script
lands flat, by basename, in the scratch directory alongside the
executable script itself. The Rscript line therefore refers to it by
a bare relative name (e.g. Rscript analysis.R), not an absolute,
home_dir-prefixed path. This is deliberate: editing the analysis
script only requires re-uploading it and resubmitting the job, with no
container rebuild or registry push in between.
Data files – data_files are the opposite case: they are baked
into the container at build time by containr::generate_dockerfile()
and live under home_dir (default "/home"). The Rscript line
passes these as absolute paths (e.g. /home/data-raw/sample.csv) so
they are found regardless of the working directory. Baking data in
rather than uploading it keeps large or unchanging reference data out
of every job's file transfer.
Writing – the script changes to HTCondor's scratch directory
(_CONDOR_SCRATCH_DIR) before creating the output folder. This
directory is writable and is where HTCondor looks for
transfer_output_files. The R script writes outputs to "output/"
using a relative path, which resolves to the scratch directory.
This separation means the R script stays portable – "output/" works
in RStudio, in quarto render, and on HTCondor – while the .sh
script handles the HTCondor-specific directory setup.
When the R script fails
The script runs under set -euo pipefail, so any failing command stops
it – except the Rscript line, which is bracketed by set +e and
set -e so that its exit status is recorded rather than fatal. The
results folder is then compressed as usual, and the script exits with
the status R returned. A failed job therefore still sends back its
tarball, holding whatever the analysis wrote before it stopped (and,
with toolero::save_output(), the accumulator row recording the failed
write), while HTCondor still sees a non-zero exit code and reports the
job as failed rather than completed. Without this, a failed job would
leave no tarball behind, and HTCondor would hold the job for a missing
transfer_output_files entry instead of letting it finish.
Relationship to htc_gen_submit()
The executable script generated by htc_gen_executable() is the file
referenced by the executable argument in htc_gen_submit(). The two
functions should always use the same mode. In "multiple" mode,
HTCondor passes each subset filename to the script as ${1}, which is
forwarded to the R script as a positional argument. The R script must
be written to accept this argument – the recommended approach is
toolero::detect_execution_context():
context <- toolero::detect_execution_context()
input_file <- switch(context,
interactive = "data-raw/sample.csv",
quarto = params$input_file,
rscript = commandArgs(trailingOnly = TRUE)[1]
)Examples
# output writes the generated .sh file; path is where the submission state
# (htc-manifest.yml) gets read from and written to. The two are
# independent arguments (see @param path), so both must point at the
# same scratch directory here to keep the submission state out of the current
# working directory.
tmp <- tempdir()
# Single-job executable script with baked-in data
htc_gen_executable(
r_script = "R/analysis.R",
data_files = "data-raw/sample.csv",
output = tmp,
path = tmp
)
# Multiple-job executable script
htc_gen_executable(
r_script = "R/analysis.R",
mode = "multiple",
output = tmp,
path = tmp
)
# Custom names with annotations
htc_gen_executable(
output_file = "run.sh",
r_script = "R/run-analysis.R",
data_files = c("data-raw/train.csv", "data-raw/test.csv"),
comments = TRUE,
verbose = TRUE,
output = tmp,
path = tmp
)
#> Warning: `output_file` ("run.sh") does not match the
#> executable script name already recorded in the
#> submission state ("job.sh").
#> ℹ That name came from an earlier `htc_gen_submit()` call.
#> ℹ If this is deliberate, ignore this warning -- this script
#> will be written as "run.sh". Otherwise, check
#> that the two calls agree on the script's name.
#> Writing shebang line
#> Writing shell options
#> Writing working directory change
#> Writing results folder creation
#> Writing Rscript execution line (mode: single)
#> Writing compression line
#> Writing exit line
#> Set executable permissions on /tmp/Rtmp4Pmd3C/run.sh
#> ✔ Executable script written to /tmp/Rtmp4Pmd3C/run.sh