1

I need to compile over 400 C programs for my job, and the only difference between the source code is a static variable in the first line of the code.

I want to write a batch file that will automatically edit and compile these 400 programs and save the .exe files in separate folders.

The compiling and moving of the .exe files is not an issue, but I can't figure out how to use a batch file to increment this static variable in the first line. The first line is along the lines of:

#define PROGRAM_NUMBER   20

and I need to change that number from 1 to 400 for each of the programs that are compiled.
Is this possible? How can I get a batch file to do this?

Thanks in advance for your help.

Currently the batch file looks like this:

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64

devenv "Project.vcxproj" /build Release

Since they are Visual Studio projects that need to be compiled.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • 6
    Are you aware of the `-D` commandline option of most compilers? – Yunnosch Jun 11 '20 at 05:21
  • No I am not. I have very little experience with running a compiler from command line. – IAlreadyHaveAKey Jun 11 '20 at 05:24
  • No problem. But you do call it from the batch file, don't you? Pleae show how exactly, I can then make my answer more specific for your case. – Yunnosch Jun 11 '20 at 05:28
  • What are you currently doing to compile those files? That is, give us something to work with. e.g. Do you already have a batch file that does some of the build steps and need to know how to extend it? – kaylum Jun 11 '20 at 05:29
  • Edited the main question to show how I'm compiling with the .bat. – IAlreadyHaveAKey Jun 11 '20 at 05:32
  • **Write a small C program running your compilation commands** (perhaps using [ninja](http://ninja-build.org/)..). So read about the [WinAPI](https://en.wikipedia.org/wiki/Windows_API) and [*Modern C*](https://modernc.gforge.inria.fr/); for Linux, see [syscalls(2)](https://man7.org/linux/man-pages/man2/syscalls.2.html) first. – Basile Starynkevitch Jun 11 '20 at 05:40
  • Would somebody be so kind to add the c-preprocessor tag (https://stackoverflow.com/questions/tagged/c-preprocessor) to the question? I admit that I want somebody else to do that because I then get the score for that tag; but not if I do it myself. (If I am not mistaken....) This is not about more reputation. – Yunnosch Jun 11 '20 at 05:59
  • Also... why do C programmers always forget that they are C programmers? If you know C, you don't need batch files. To open a text file, replace a line and save it again, ain't a hard problem to solve for the average C programming beginner. You can have a program do that in a loop, then call the compiler with system() from the same loop. It's a pretty slow solution, but very simple, and this doesn't sound like a task you'd do frequently. – Lundin Jun 11 '20 at 06:57

2 Answers2

4

You can use the -D command line option to pass a value to a macro-definition from the commandline which calls the compiler.

https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html

-D name=definition The contents of definition are tokenized and processed as if they appeared during translation phase three in a ‘#define’ directive. In particular, the definition is truncated by embedded newline characters.

If you are invoking the preprocessor from a shell or shell-like program you may need to use the shell’s quoting syntax to protect characters such as spaces that have a meaning in the shell syntax.

If you wish to define a function-like macro on the command line, write its argument list with surrounding parentheses before the equals sign (if any). Parentheses are meaningful to most shells, so you should quote the option. With sh and csh, -D'name(args…)=definition' works.

-D and -U options are processed in the order they are given on the command line. All -imacros file and -include file options are processed after all -D and -U options.

I.e. if you normally call it (very simplified) like

gcc myfile.c

then, if you call like

gcc -D PROGRAM_NUMBER=20 myfile.c

it will basically compile the same, but also as if the file starts with

#define PROGRAM_NUMBER 20

in the very first line.

Nice recommendation by Basile, quote:
With GCC compile with warnings and debug info, so

gcc -Wall -Wextra -g -DPROGRAM_NUMBER=20

and use the GDB debugger
end-of-quote

That in turn means you do not need your many copies of the file with different #defines, you can just use the same one over and over again.

I just recommend to start that one with this sanity check:

#ifndef PROGRAM_NUMBER
#error The PROGRAM_NUMBER has to be defined!
#endif

For Microsoft /D seems to do the trick:
https://learn.microsoft.com/en-us/cpp/build/reference/d-preprocessor-definitions?view=vs-2019

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • 1
    I've edited the question to show how I'm currently compiling the projects in the batch file. – IAlreadyHaveAKey Jun 11 '20 at 05:36
  • Have added a MS hint, trying to find details. – Yunnosch Jun 11 '20 at 05:39
  • With [GCC](http://gcc.gnu.org/) compile with warnings and debug info, so `gcc -Wall -Wextra -g -DPROGRAM_NUMBER=2` and use the [GDB](https://sourceware.org/gdb/current/onlinedocs/gdb/) debugger – Basile Starynkevitch Jun 11 '20 at 05:44
  • I will pick that up, assuming you do not mind @BasileStarynkevitch – Yunnosch Jun 11 '20 at 05:47
  • The same docs that mention `/D` also confirm that `#ifndef`...`#endif` and `#error` should be supported - https://learn.microsoft.com/en-us/cpp/preprocessor/preprocessor-directives?view=vs-2019 – alani Jun 11 '20 at 05:50
2

Like gcc having a -D command line option, MSVC has /D:

https://learn.microsoft.com/en-us/cpp/build/reference/d-preprocessor-definitions?view=vs-2019

However, devenv doesn't support /D switch. As a workaround, set CL environmental variable, which is used by the CL compiler.

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64

SET CL=/DPROGRAM_NUMBER#20

devenv "Project.vcxproj" /rebuild Release

You can't use the /D option to define a symbol that uses an equal sign (=). Instead, you can use the number sign (#) for an equal sign. In this way, you can use the CL or CL environment variables to define preprocessor constants with explicit values—for example, /DDEBUG#1 to define DEBUG=1.

https://learn.microsoft.com/en-us/cpp/build/reference/cl-environment-variables

And if PROGRAM_NUMBER is a counter (e.g., from 1 to 400), then you can use a for loop instead of duplicating the line 400 times.

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64

for /L %%a in (1,1,400) do (
    echo Building program %%a
    SET CL=/DPROGRAM_NUMBER#%%a
    devenv "Project.vcxproj" /rebuild Release
)

/rebuild needs to be used to force the compiler to build the project even though the files involved aren't actually being edited.

Counting in a FOR loop using Windows Batch script

As @Yunnosch suggested, add the following check to your code to make sure the PROGRAM_NUMBER is defined:

#ifndef PROGRAM_NUMBER
# error The PROGRAM_NUMBER has to be defined!
#endif
PooSH
  • 601
  • 4
  • 11
  • Thanks for your input. Please believe that I found that info myself for my answer. Though slightly later than you. – Yunnosch Jun 11 '20 at 05:40
  • 1
    Should you be familiar and could provide how to adapt OPs way of doing compiling from batch, that would be very helpful and probably deserve being accepted instead of my answer. Especially if you can test it and report success. Which I will not be able to do. – Yunnosch Jun 11 '20 at 05:41
  • Showing the batch counter via `for` is good. The actual commandline to call the compiler with a counting `#define`would be the final touch. If it does not seem too vain I recommend to refer to the sanity check code I proposed in my answer. Then your answer is the complete MS solution. – Yunnosch Jun 11 '20 at 05:44
  • This doesn't work for me, I get the error "Invalid Command Line. Unknown Swithc : D" – IAlreadyHaveAKey Jun 11 '20 at 05:49
  • Sorry, extra space. I edited the answer. `/DPROGRAM_NUMBER=20` – PooSH Jun 11 '20 at 05:57
  • I don't think that calling `vcvarsall. bat` is required in every iteration. It sets the environment variables. Most-likely, it should be called only once, then the for loop. – PooSH Jun 11 '20 at 06:14
  • I see, thanks. Would you like to add that line before the `for` then? It would save people from the confusion I suffered. – Yunnosch Jun 11 '20 at 07:34
  • I get the same error when I run it without the space as well except it says "unknown switch : DPROGRAM_NUMBER" – IAlreadyHaveAKey Jun 11 '20 at 08:45
  • Sorry, I was too naive to believe that Microsoft can make something easy. It appears that `devenv` doesn't forward command-line arguments to the compiler, neither provides it sown alternative for `/D`. As a workaround, you can set the `CL` environment variable. I have updated the answer. Please try that. – PooSH Jun 11 '20 at 09:28
  • This now works for the first case but it doesn't update PROGRAM_NUMBER the second time, even if I just try the first code (without the loop) if I edit the batch file to a different program number and then run it, it doesn't update the PROGRAM_NUMBER in the compile. – IAlreadyHaveAKey Jun 12 '20 at 01:19
  • Need to use /rebuild instead of /build since the files in the project don't change between builds so /build won't build it. – IAlreadyHaveAKey Jun 15 '20 at 00:33