Expanding subdirectories in a batch file
8/24/2021
When I am working on C++ projects the way I compile my projects is by creating a shell script or a batch file and compiling the whole project every single time. Many C++ developers might not like this approach arguing that it increases compiling times substantially but in my experience this is not an issue if you write code in a sensible way without abusing features like templates and many other ways you can help the compiler be faster when compiling your code. The other approach is to use some build system like GNU Make or any of the other existing solutions. I find the script approach simpler even though it has its caveats like the one I'm going to talk about.
If I'm compiling on Windows I need to pass the .cpp
files to MSVC compiler so it know where the definitions of my functions are, something like this:
cl src\\\*.cpp
and this will work if you have all your .cpp
files in src/
in this case. But if you organize your project in subdirectories you'll find that there is no obvious way to resolve multiple wildcards in the path like you would do in a shell script:
g++ src/**/*.cpp
This works as you would expect by expanding all the .cpp
files even in subdirectories. In a batch file we cannot do that so we need to use a command to output the .cpp
files for us. dir
can do it like this:
dir src\\\*.cpp /s /b
This command will return to us all the .cpp
files inside src/
with the /s
flag we tell the command we want to display the files in the specified directory and all subdirectories and the /b
flag tells dir
to use bare format (the file path without any heading or summary information).
It will return something like this:
D:\\\dev\\\Game\\\src\\\main.cpp
D:\\\dev\\\Game\\\src\\\game\\\graphics.cpp
D:\\\dev\\\Game\\\src\\\game\\\input.cpp
This is the output we want but now we need to find a way to pass it to our compiler. In unix-like system it would be as easy as using backquoting to store it in a variable and pass that variable to the compiler:
CPPS=`dir src\\\*.cpp /s /b`
g++ $CPPS
Again we don't have a straightforward way to store the output of a command in a variable using the Windows command processor but we can fake it with the use of the FOR
command.
for /f %%i in ('dir src\\\*.cpp /s /b') do echo %%i
It will loop through the output line by line and assign it each time to the i
variable. So we could use it to loop through the output and store it in a variable.
set CPPS=
for /f %%i in ('dir src\\\*.cpp /s /b') do set CPPS=%CPPS% %%i
cl %CPPS%
This looks like it should work. We should have the accumulated result in CPPS and we pass it to cl. We are getting close but there's still a small issue in the body of our FOR
. The Windows command processor only evaluates and expands variables at parse time before each line is executed so every time we execute set CPPS=%CPPS% %i
we will see that %CPPS% is empty so we need to solve it by enabling delayed expansion which will cause the variables being expanded at execution time by enabling !var!
as a way of expanding the variable each time it is executed inside the FOR
.
Our final script looks like this:
setlocal EnableDelayedExpansion
set CPPS=
for /f %%i in ('dir src\\\*.cpp /s /b') do set CPPS=!CPPS! %%i
cl %CPPS%
Maybe you don't compile code the same way as I do but if you are ever writing a batch file and need a way to do this kind of wildcard expansions, this could be a way of doing it.