It looks like QMAKE_POST_LINK works well for this sort of thing.
This seems to get the job done. my_batch_file.bat runs when nmake runs (rather than when qmake runs) and I don’t need to do anything funny with placeholder targets or files.
It’s quite likely that I don’t need all of the items listed in ‘CONFIG’.
TEMPLATE = lib
TARGET =
CONFIG += no_link target_predeps staticlib
QMAKE_POST_LINK = my_batch_file.bat
QMAKE_CLEAN += batch_output.obj