There are several things that can make builds nondeterministic:
* Many file formats, especially (but not exclusively) archives like .zip, embed timestamps in them.
* Absolute paths are also ridiculously easy to get embedded in the build artifact.
* Other components of the user's environment can get embedded, including current locale, username, hostname. Some of these are more common than others.
* Using an actual random number generator is more common than you might think (e.g., generating a cryptographic key during the build process), although still pretty rare.
* Saying 'for file in directory' is usually sorted in inode order by default, which makes it inconsistent on different people's drives (although deterministic for a single user, generally).
* Pointer addresses are nondeterministic. And it's easy to accidentally make a container that sorts by pointer address (e.g., std::map in C++).
* In general, there's several features of programming languages that make it easy to accidentally inject nondeterminism in your program. Concurrency is obvious, but using standard containers like rust's HashMap or C++'s std::map combined with a "for every value in container" make it easy to get that nondeterminism. Compilers are not immune from being nondeterministic, and while it is a bug for them to be nondeterministic, it still happens.