Skip to content

Commit 4c52f46

Browse files
authored
feat: custom build folder (#1173)
2 parents e9f9476 + db4b2e4 commit 4c52f46

File tree

10 files changed

+213
-36
lines changed

10 files changed

+213
-36
lines changed

ci/run_tests.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,5 +366,8 @@ pushd both_lib_types
366366
test $(ls lib/libboth_lib_types* | wc -l) -eq 2
367367
popd
368368

369+
# Test custom build directory functionality
370+
bash "../ci/test_custom_build_dir.sh" "$fpm" hello_world
371+
369372
# Cleanup
370373
rm -rf ./*/build

ci/test_custom_build_dir.sh

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env bash
2+
set -ex
3+
4+
# Test script for custom build directory functionality
5+
# Usage: ./test_custom_build_dir.sh [fpm_executable] [example_package_dir]
6+
7+
if [ "$1" ]; then
8+
fpm="$1"
9+
else
10+
fpm=fpm
11+
fi
12+
13+
if [ "$2" ]; then
14+
test_package="$2"
15+
else
16+
test_package="hello_world"
17+
fi
18+
19+
echo "Testing custom build directory functionality with package: $test_package"
20+
21+
# Test 1: Custom build directory with CLI option
22+
pushd "$test_package"
23+
echo "Test 1: CLI option --build-dir"
24+
rm -rf ./build custom_build_test
25+
"$fpm" build --build-dir custom_build_test
26+
test -d custom_build_test
27+
test -f custom_build_test/.gitignore
28+
"$fpm" run --build-dir custom_build_test --target "$test_package"
29+
# Verify standard build directory was not created
30+
test ! -d build
31+
echo "✓ CLI option --build-dir works"
32+
33+
# Test 2: Environment variable
34+
echo "Test 2: Environment variable FPM_BUILD_DIR"
35+
rm -rf custom_build_test env_build_test
36+
FPM_BUILD_DIR=env_build_test "$fpm" build
37+
test -d env_build_test
38+
test -f env_build_test/.gitignore
39+
FPM_BUILD_DIR=env_build_test "$fpm" run --target "$test_package"
40+
echo "✓ Environment variable FPM_BUILD_DIR works"
41+
42+
# Test 3: CLI option overrides environment variable
43+
echo "Test 3: CLI option overrides environment variable"
44+
rm -rf env_build_test cli_override_test
45+
FPM_BUILD_DIR=env_build_test "$fpm" build --build-dir cli_override_test
46+
test -d cli_override_test
47+
test ! -d env_build_test
48+
echo "✓ CLI option correctly overrides environment variable"
49+
50+
# Test 4: Build directory validation - reserved names
51+
echo "Test 4: Build directory validation"
52+
# These should fail with specific error messages
53+
if "$fpm" build --build-dir src 2>&1 | grep -q "conflicts with source directory"; then
54+
echo "✓ Correctly rejected 'src'"
55+
else
56+
echo "ERROR: Should reject 'src'" && exit 1
57+
fi
58+
59+
if "$fpm" build --build-dir app 2>&1 | grep -q "conflicts with source directory"; then
60+
echo "✓ Correctly rejected 'app'"
61+
else
62+
echo "ERROR: Should reject 'app'" && exit 1
63+
fi
64+
65+
if "$fpm" build --build-dir test 2>&1 | grep -q "conflicts with source directory"; then
66+
echo "✓ Correctly rejected 'test'"
67+
else
68+
echo "ERROR: Should reject 'test'" && exit 1
69+
fi
70+
71+
if "$fpm" build --build-dir . 2>&1 | grep -q "would overwrite the current"; then
72+
echo "✓ Correctly rejected '.'"
73+
else
74+
echo "ERROR: Should reject '.'" && exit 1
75+
fi
76+
77+
# Test 5: Path normalization
78+
echo "Test 5: Path normalization"
79+
if "$fpm" build --build-dir ./src 2>&1 | grep -q "conflicts with source directory"; then
80+
echo "✓ Correctly rejected './src' (path normalization works)"
81+
else
82+
echo "ERROR: Should reject './src'" && exit 1
83+
fi
84+
85+
# Test 6: Different commands with custom build directory
86+
echo "Test 6: Different commands with custom build directory"
87+
rm -rf test_build_all
88+
"$fpm" build --build-dir test_build_all
89+
"$fpm" run --build-dir test_build_all --target "$test_package"
90+
# Some packages may not have tests, so this might fail but that's expected
91+
"$fpm" test --build-dir test_build_all 2>/dev/null || echo "No tests in $test_package (expected)"
92+
echo "✓ All commands work with custom build directory"
93+
94+
# Cleanup test directories
95+
rm -rf custom_build_test env_build_test cli_override_test test_build_all
96+
popd
97+
98+
echo "All custom build directory tests passed!"

src/fpm.f90

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ subroutine build_model(model, settings, package, error)
6969
end if
7070

7171
call new_compiler_flags(model,settings)
72-
model%build_prefix = join_path("build", basename(model%compiler%fc))
72+
model%build_dir = settings%build_dir
73+
model%build_prefix = join_path(settings%build_dir, basename(model%compiler%fc))
7374
model%include_tests = settings%build_tests
7475
model%enforce_module_names = package%build%module_naming
7576
model%module_prefix = package%build%module_prefix
@@ -79,8 +80,8 @@ subroutine build_model(model, settings, package, error)
7980
if (allocated(error)) return
8081

8182
! Create dependencies
82-
call new_dependency_tree(model%deps, cache=join_path("build", "cache.toml"), &
83-
& path_to_config=settings%path_to_config)
83+
call new_dependency_tree(model%deps, cache=join_path(settings%build_dir, "cache.toml"), &
84+
& path_to_config=settings%path_to_config, build_dir=settings%build_dir)
8485

8586
! Build and resolve model dependencies
8687
call model%deps%add(package, error)
@@ -90,9 +91,9 @@ subroutine build_model(model, settings, package, error)
9091
call model%deps%update(error)
9192
if (allocated(error)) return
9293

93-
! build/ directory should now exist
94-
if (.not.exists("build/.gitignore")) then
95-
call filewrite(join_path("build", ".gitignore"),["*"])
94+
! build directory should now exist
95+
if (.not.exists(join_path(settings%build_dir, ".gitignore"))) then
96+
call filewrite(join_path(settings%build_dir, ".gitignore"),["*"])
9697
end if
9798

9899
allocate(model%packages(model%deps%ndep))

src/fpm/cmd/export.f90

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ subroutine cmd_export(settings)
4444
if (len_trim(settings%dump_dependencies)>0) then
4545

4646
!> Generate dependency tree
47-
filename = join_path("build", "cache.toml")
48-
call new_dependency_tree(deps, cache=filename, verbosity=merge(2, 1, settings%verbose))
47+
filename = join_path(settings%build_dir, "cache.toml")
48+
call new_dependency_tree(deps, cache=filename, verbosity=merge(2, 1, settings%verbose), build_dir=settings%build_dir)
4949
call deps%add(package, error)
5050
call handle_error(error)
5151

src/fpm/cmd/update.f90

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module fpm_cmd_update
2-
use fpm_command_line, only : fpm_update_settings
2+
use fpm_command_line, only : fpm_update_settings, get_fpm_env
33
use fpm_dependency, only : dependency_tree_t, new_dependency_tree
44
use fpm_error, only : error_t, fpm_stop
55
use fpm_filesystem, only : exists, mkdir, join_path, delete_file, filewrite
@@ -20,21 +20,24 @@ subroutine cmd_update(settings)
2020
type(dependency_tree_t) :: deps
2121
type(error_t), allocatable :: error
2222
integer :: ii
23-
character(len=:), allocatable :: cache
23+
character(len=:), allocatable :: cache, build_dir
2424

2525
call get_package_data(package, "fpm.toml", error, apply_defaults=.true.)
2626
call handle_error(error)
2727

28-
if (.not. exists("build")) then
29-
call mkdir("build")
30-
call filewrite(join_path("build", ".gitignore"),["*"])
28+
! Get build directory from environment variable or use default
29+
build_dir = get_fpm_env("BUILD_DIR", "build")
30+
31+
if (.not. exists(build_dir)) then
32+
call mkdir(build_dir)
33+
call filewrite(join_path(build_dir, ".gitignore"),["*"])
3134
end if
3235

33-
cache = join_path("build", "cache.toml")
36+
cache = join_path(build_dir, "cache.toml")
3437
if (settings%clean) call delete_file(cache)
3538

3639
call new_dependency_tree(deps, cache=cache, verbosity=merge(2, 1, settings%verbose), &
37-
& path_to_config=settings%path_to_config)
40+
& path_to_config=settings%path_to_config, build_dir=build_dir)
3841

3942
call deps%add(package, error)
4043
call handle_error(error)

src/fpm/dependency.f90

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ module fpm_dependency
205205
contains
206206

207207
!> Create a new dependency tree
208-
subroutine new_dependency_tree(self, verbosity, cache, path_to_config)
208+
subroutine new_dependency_tree(self, verbosity, cache, path_to_config, build_dir)
209209
!> Instance of the dependency tree
210210
type(dependency_tree_t), intent(out) :: self
211211
!> Verbosity of printout
@@ -214,9 +214,15 @@ subroutine new_dependency_tree(self, verbosity, cache, path_to_config)
214214
character(len=*), intent(in), optional :: cache
215215
!> Path to the global config file.
216216
character(len=*), intent(in), optional :: path_to_config
217+
!> Custom build directory
218+
character(len=*), intent(in), optional :: build_dir
217219

218220
call resize(self%dep)
219-
self%dep_dir = join_path("build", "dependencies")
221+
if (present(build_dir)) then
222+
self%dep_dir = join_path(build_dir, "dependencies")
223+
else
224+
self%dep_dir = join_path("build", "dependencies")
225+
end if
220226

221227
if (present(verbosity)) self%verbosity = verbosity
222228

src/fpm_backend.F90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ subroutine build_package(targets,model,verbose,dry_run)
115115
plain_output = .true.
116116
#endif
117117

118-
progress = build_progress_t(queue,plain_output)
118+
progress = build_progress_t(queue,plain_output,model%build_dir)
119119

120120
! Loop over parallel schedule regions
121121
do i=1,size(schedule_ptr)-1

src/fpm_backend_output.f90

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ module fpm_backend_output
3333
logical :: plain_mode = .true.
3434
!> Store needed when updating previous console lines
3535
integer, allocatable :: output_lines(:)
36+
!> Build directory
37+
character(:), allocatable :: build_dir
3638
!> Queue of scheduled build targets
3739
type(build_target_ptr), pointer :: target_queue(:)
3840
!> The compile_commands.json table
@@ -56,11 +58,13 @@ module fpm_backend_output
5658
contains
5759

5860
!> Initialise a new build progress object
59-
function new_build_progress(target_queue,plain_mode) result(progress)
61+
function new_build_progress(target_queue,plain_mode,build_dir) result(progress)
6062
!> The queue of scheduled targets
6163
type(build_target_ptr), intent(in), target :: target_queue(:)
6264
!> Enable 'plain' output for progress object
6365
logical, intent(in), optional :: plain_mode
66+
!> Build directory
67+
character(*), intent(in), optional :: build_dir
6468
!> Progress object to initialise
6569
type(build_progress_t) :: progress
6670

@@ -71,6 +75,12 @@ function new_build_progress(target_queue,plain_mode) result(progress)
7175
progress%plain_mode = plain_mode
7276
progress%n_complete = 0
7377

78+
if (present(build_dir)) then
79+
progress%build_dir = build_dir
80+
else
81+
progress%build_dir = "build"
82+
end if
83+
7484
allocate(progress%output_lines(progress%n_target))
7585

7686
end function new_build_progress
@@ -191,7 +201,7 @@ subroutine output_write_compile_commands(progress,error)
191201
type(error_t), allocatable :: error
192202

193203
! Write compile commands
194-
path = join_path('build','compile_commands.json')
204+
path = join_path(progress%build_dir,'compile_commands.json')
195205

196206
call progress%compile_commands%write(filename=path, error=error)
197207

0 commit comments

Comments
 (0)