-D on command line for a few parameters, or those that I need to build different versions with.
For example, if I have two PCB revisions on the field (and those can't be disposed of), then it's -DPCBREV=1B.
Or if you have two versions with different battery voltage and the firmware needs to know what it is to correctly implement battery indication, and you don't want to do it runtime configurable parameter, then -D is your friend, you always know what you are compiling and can quickly do different versions.
For large number of mostly rarely changing constants and #defines, some config.h that can be included from multiple files. Everything must be static const, or #defines, so they work over multiple files. I prefer one such config.h even if these are used in multiple places. Everything here should be truly something that can and will be modified; for stuff where change of constant requires code review or code changes, just define them in the related module (thing.c), close to where they are used. If module becomes too long for you to "find" the constant definitions, don't try to mitigate by moving them to a "common place", instead split the module functionality.
These are all simplifications for small projects on small teams, larger solutions involve configuration scripts (think about configure-make-install pattern).