diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ca2376a64..8a68d05e7 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -4,6 +4,9 @@ "hostRequirements": { "cpus": 4 }, + "runArgs": [ + "--cap-add=SYS_PTRACE" + ], "customizations": { "vscode": { "settings": { @@ -12,8 +15,8 @@ "editor.detectIndentation": false, "r.lsp.diagnostics": false, "r.plot.useHttpgd": true, + "r.rterm.linux": "/workspaces/r-dev-env/scripts/launch_r.sh", "r.rpath.linux": "/usr/bin/R", - "r.rterm.linux": "/usr/bin/R", "terminal.integrated.sendKeybindingsToShell": true, "svn.multipleFolders.enabled": true, "workbench.editorAssociations": { @@ -28,9 +31,10 @@ "johnstoncode.svn-scm", "ms-vscode.cpptools", "MS-vsliveshare.vsliveshare", - "natqe.reload" + "natqe.reload", + "vadimcn.vscode-lldb" ] } }, - "postCreateCommand": "find . -wholename '*.git*' -type d -prune -o -type f -exec chown vscode:vscode {} \\; && sh /workspaces/r-dev-env/scripts/localscript.sh" +"postCreateCommand": "find . -wholename '.git*' -type d -prune -o -type f -exec chown vscode:vscode {} \\; && sh ./scripts/localscript.sh" } diff --git a/.devcontainer/launch.json b/.devcontainer/launch.json new file mode 100644 index 000000000..210901430 --- /dev/null +++ b/.devcontainer/launch.json @@ -0,0 +1,12 @@ +{ +"version": "0.2.0", +"configurations": [ + { + "name": "(lldb) Attach to R", + "type": "lldb", + "request": "attach", + "pid": "${command:pickMyProcess}", + "stopOnEntry": false + } + ] +} diff --git a/docs/tutorials/building_r.md b/docs/tutorials/building_r.md index 85f68146a..c49ca60e5 100644 --- a/docs/tutorials/building_r.md +++ b/docs/tutorials/building_r.md @@ -55,7 +55,22 @@ mkdir -p $BUILDDIR cd $BUILDDIR ``` -**5) Configure the build** +**5) Set CFLAGS (Optional—For Debugging C Code)** + +- **This step is optional and recommended for those who want to debug C code.** +- Set the `CFLAGS` environment variable before running configure: + +```bash +CFLAGS="-g -O0" +``` + +- These flags modify the configuration defined in the next step, +so that when R is built, C code will be compiled with debugging +symbols (`-g`) and compiler optimizations will be disabled +(`-O0`) so that the structure of the code closely matches the +original source. + +**6) Configure the build** - After we change directory, we must run the configure script from the source directory. This step takes ~1 minute on the codespace. @@ -80,7 +95,7 @@ $TOP_SRCDIR/configure --with-valgrind-instrumentation=1 ![alt text](../assets/rdev7.png) -**6) Build R** +**7) Build R** Having configured R, we run `make` to build R. This take 5-10 minutes on the codespace. @@ -89,7 +104,7 @@ codespace. make ``` -**7) Check R** +**8) Check R** Check that the build of R passes R's standard checks: @@ -101,7 +116,7 @@ This takes a couple of minutes in the codespace. The check will stop with a error message if any of the tests fail. If this happens, see [SVN Help](./svn_help.md) for how to revert to a version that passes check. -**8) Make R terminals use the built R** +**9) Make R terminals use the built R** Run the `which_r` script to set which R to use for R terminals in VSCode. When prompted, enter the number corresponding to `r-devel` @@ -125,7 +140,7 @@ built![^1] selected version is saved in the VSCode settings, so will be saved when you stop and restart the codespace. -**9) Make contributions** +**10) Make contributions** - After having built the current development version of R, we can now make changes to the source code and contribute to the project. diff --git a/scripts/allow_ptrace.c b/scripts/allow_ptrace.c new file mode 100644 index 000000000..f730619f0 --- /dev/null +++ b/scripts/allow_ptrace.c @@ -0,0 +1,6 @@ +#include + +__attribute__((constructor)) +static void allow_ptrace(void) { + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); +} diff --git a/scripts/allow_ptrace.so b/scripts/allow_ptrace.so new file mode 100755 index 000000000..961d1d6fa Binary files /dev/null and b/scripts/allow_ptrace.so differ diff --git a/scripts/launch_r.sh b/scripts/launch_r.sh new file mode 100644 index 000000000..336919f15 --- /dev/null +++ b/scripts/launch_r.sh @@ -0,0 +1,3 @@ +#!/bin/bash +export LD_PRELOAD=/workspaces/r-dev-env/scripts/allow_ptrace.so +exec /usr/bin/R "$@" diff --git a/scripts/localscript.sh b/scripts/localscript.sh index 3797814b7..7a5c4c585 100644 --- a/scripts/localscript.sh +++ b/scripts/localscript.sh @@ -9,6 +9,7 @@ local_script(){ WORK_DIR=$PWD VSCODE_DIR="$WORK_DIR/.vscode" DEVCONTAINER_JSON="$WORK_DIR/.devcontainer/devcontainer.json" +SCRIPTS_DIR="$WORK_DIR/scripts" # Create patch directory in workspace root ($PWD at start) PATCHDIR="$WORK_DIR/patches" @@ -16,14 +17,13 @@ mkdir -p $PATCHDIR mkdir -p $VSCODE_DIR # Copy the which_r and set_build_r function definitions to .bashrc -cat $WORK_DIR/scripts/which_r.sh >> ~/.bashrc -cat $WORK_DIR/scripts/set_build_r.sh >> ~/.bashrc +cat $SCRIPTS_DIR/which_r.sh >> ~/.bashrc +cat $SCRIPTS_DIR/set_build_r.sh >> ~/.bashrc # Copy over the welcome message script to be run when bash terminal starts -cat $WORK_DIR/scripts/welcome_msg.sh >> ~/.bashrc +cat $SCRIPTS_DIR/welcome_msg.sh >> ~/.bashrc #bash ~/.bashrc - # Remove git directory if it exists rm -rf .git @@ -38,3 +38,9 @@ fi # Run the main function local_script + +# 1. Build the ptrace helper library +gcc -shared -fPIC -o "$SCRIPTS_DIR/allow_ptrace.so" "$SCRIPTS_DIR/allow_ptrace.c" + +# 2. Mark the wrapper executable +chmod +x "$SCRIPTS_DIR/launch_r.sh" diff --git a/scripts/which_r.sh b/scripts/which_r.sh index 689aecf60..f9002f9b7 100644 --- a/scripts/which_r.sh +++ b/scripts/which_r.sh @@ -1,68 +1,53 @@ - +#!/usr/bin/env bash which_r() { - # Specify the parent directory - parent_dir="$WORK_DIR/build" - - # Path to the settings.json file - settings_file_path=$WORK_DIR/.vscode/settings.json + parent_dir="$PWD/build" + settings_file_path="$PWD/.vscode/settings.json" + launch_script="$PWD/scripts/launch_r.sh" - built_in_r_version=$(R --version | grep "^R version" | awk '{print $3}') + built_in_r_version=$(R --version | awk '/^R version/ {print $3}') - # Ask user which R version to use echo "Which version of R should be used in new R terminals?" - echo " 1. R $built_in_r_version (release version built into this container)" + echo " 1. R $built_in_r_version (built-in)" - # Check for additional R versions in subdirectories + # Detect additional R builds + subdirs=() + counter=2 if [ -d "$parent_dir" ]; then - # Create an array to store subdirectory names - subdirs=() - - # Loop through subdirectories and print numbered list - counter=2 # Start counter at 2 to avoid conflict with built-in R for dir in "$parent_dir"/*; do - if [ -d "$dir/bin" ] && [ -x "$dir/bin/R" ]; then - subdir=$(basename "$dir") - subdirs+=("$subdir") # Populate subdirs array - echo " $counter. $subdir" + if [ -x "$dir/bin/R" ]; then + subdirs+=("$(basename "$dir")") + echo " $counter. ${subdirs[-1]}" ((counter++)) fi done fi - # If no additional R builds were found - if [ ${#subdirs[@]} -eq 0 ]; then - range=1 - echo "No additional R builds available." - else - range=$((counter - 1)) - fi + range=$((counter - 1)) + [ "${#subdirs[@]}" -eq 0 ] && echo "No additional R builds found." - # Get user choice - read -p "Enter the number corresponding to the selected version: " choice + read -p "Enter number (1–$range): " choice - # Define selected version based on choice - if [[ "$choice" -eq 1 ]]; then - # Use built-in R - selected_version="/usr/bin/R" - elif [[ "$choice" -ge 2 ]] && [[ "$choice" -lt "$counter" ]]; then - # Use R from chosen subdirectory - chosen_subdir="${subdirs[((choice - 2))]}" - selected_version="$parent_dir/$chosen_subdir/bin/R" + if [ "$choice" -eq 1 ] 2>/dev/null; then + selected="/usr/bin/R" + elif [ "$choice" -ge 2 ] && [ "$choice" -le "$range" ] 2>/dev/null; then + idx=$((choice - 2)) + selected="$parent_dir/${subdirs[$idx]}/bin/R" else - # Invalid choice, default to built-in R - if [[ $range -eq 1 ]]; then - echo "Invalid choice, please enter 1. Defaulting to built-in R version." - else - echo "Invalid choice, please select options between 1 to $range. Defaulting to built-in R version." - fi - selected_version="/usr/bin/R" + echo "Invalid choice; defaulting to built-in" + selected="/usr/bin/R" fi - # Update settings.json with the chosen R path - updated_settings_data=$(cat "$settings_file_path" | jq --arg subdir "$selected_version" '."r.rterm.linux"=$subdir | ."r.rpath.linux"=$subdir') - echo "$updated_settings_data" > "$settings_file_path" + # Update launch_r.sh to call the selected R + sed -i "s|^exec .*/R|exec $selected|" "$launch_script" + + # Update VS Code setting if it exists + if [ -f "$settings_file_path" ]; then + jq --arg r "$selected" '."r.rpath.linux"=$r' \ + "$settings_file_path" > "${settings_file_path}.tmp" \ + && mv "${settings_file_path}.tmp" "$settings_file_path" + fi - echo "R terminal will now use version: $selected_version" - echo "To update the HTML help, click \"Reload\" in the VS Code status bar (bottom right) to reload your VS Code window." + echo "Now using R at: $selected" + echo "Reload VS Code to apply updates." }