Ok, I don't want to be too much of a pain - I can see the way things have gone and my attitude is "oh well" but I thought I'd put the case anyhow:
1) sem_timedwait() in posix lets you timeout so in a big build when something crashes or just sits around, there is at least the option of printing an error message or giving up on locking from that point on which is a bit of a godsend if it happens overnight and there's no-one to restart the build. With stdio and file locks I don't know if this is a problem anyhow for crashes (are locks automatically released?) but the behaviour between windows and linux might be different.
2) Semaphores have the semantics you want a) the ability to lock b) system-wide c) named so that processes can access them. So it seems simpler to me to use that semantic as the model and implement it in whatever is the most faithful way for each platform.
3) As for POSIX semaphores, I found that one could do a lot by just having init and delete functions that created and destroyed them at the start and end of make execution. Only the parent make does this of course. You can make them live in /tmp which is volatile anyhow and you pre-delete ones that by incredible luck might exist before make started and have the same name as the one you want to use.
4) In a way this makes the file-lock and semaphore mechanisms "look" rather the same - files in /tmp. This seems easier to understand.