Opened 6 years ago

Closed 6 years ago

#32426 closed defect (fixed)

angelscript: sometimes rebuilds in destroot phase, causing wrong library install_name

Reported by: ryandesign (Ryan Schmidt) Owned by: rudloff@…
Priority: Normal Milestone:
Component: ports Version: 2.0.3
Keywords: Cc:
Port: angelscript

Description

The angelscript port added in r87787 has an intermittent problem where it will sometimes rebuild the libraries in the destroot phase. When it does this, it might use an incorrect compiler (i.e. if the user had selected a different compiler than the default, because we're only setting the CXX environment variable in the build phase and not in the destroot phase), and it will definitely write the wrong install_name into the library (because it's based on the LOCAL variable which is prefixed with ${destroot} in the destroot phase).

The reason this happens is because of the way the makefile is written. In the notes I will explain the problem in more detail and show how I'm fixing it. This is an upstream problem; the developers should fix it in their sources (though I won't claim that my method is the best way to fix it).

Change History (2)

comment:1 Changed 6 years ago by ryandesign (Ryan Schmidt)

The makefile defines a variable TARG representing all the targets that will be built and installed (i.e. the three libraries):

TARG = $(LIBDIR)/$(LIB) $(LIBDIR)/$(DEVLIB) $(LIBDIR)/$(BUNDLE) 

The all and install rules depend on this variable, and thus on all three libraries:

all: $(TARG)
...
install: $(TARG)

There are also two rules to create directories: the object directory (where objects get built before they're packed into a library) and the library directory (where the libraries go after they're built):

$(OBJDIR):
	mkdir $(OBJDIR)

$(LIBDIR):
	mkdir $(LIBDIR)

There are also variables OBJ and BOBJ containing the lists of objects needed for the libraries, not relevant to the problem.

The three libraries are defined as depending on the creation of the directories, and on their list of objects, as follows:

$(LIBDIR)/$(LIB): $(OBJDIR) $(LIBDIR) $(OBJ)
	...
$(LIBDIR)/$(DEVLIB): $(OBJDIR) $(LIBDIR) $(OBJ)
	...
$(LIBDIR)/$(BUNDLE): $(OBJDIR) $(LIBDIR) $(BOBJ)
	...

The problem comes about because of the way make works, and the way directories work. The way make works is that for each rule, it determines whether the file (or directory) that the rule is for exists. If not, it deals with the dependencies, and then makes the rule. If the file the rule is for does exist, it looks at the timestamps of that file, and of the dependencies' files, and of the makefile; if the makefile or the dependencies are newer, then the rule is remade. The way directories work is that if you add or remove a file in the directory, the directory's modification time is updated.

The error is always reproducible in a serial build (unless you have a computer that's 10-20 times faster than mine). The sequence of events is as follows:

  • T1
    • MacPorts runs make all, which depends on TARG, which expands to the three libraries.
  • T2
    • The dependencies of the first library are evaluated. Neither the object directory nor the library directory exist, so those rules are run, thus creating those directories. Their modification time is T2.
  • T3
    • The first object is built, in the object directory. The act of creating the object file in the object directory causes the object directory's modification time to be updated to T3.
  • ... similarly for the other object files. This takes a couple dozen seconds on my system. As each object is built, the object directory's modification time advances.
  • T4
    • The first library is built. This takes some seconds. It gets put into the library directory. This causes the library directory's modification time to be updated and is now T4.
  • T5
    • The dependencies of the second library are evaluated. The directories already exist; no action necessary. The objects are the same as the first library's so they're already built too.
  • T6
    • The second library is built. This takes some seconds. It gets put into the library directory, thus updating the library directory's modification time to T6.
  • ... similarly with the third library
  • T7
    • MacPorts runs make install, which depends on TARG, which expands to the three libraries.
  • T8
    • The first library exists; its modification time is T4. The dependencies of the first library are evaluated. The object directory exists and its modification time is somewhere between T3 and T4 which is older than the library so we move on. The library directory exists, but its modification time is T6. This is newer than the library's T4 modification time, so make rebuilds the first library, and there we have the problem.

The reason the problem is intermittent is that MacPorts runs a parallel build by default -- and not a serial build -- provided you have enough memory and processor cores. On my system, the libraries were either finishing up their simultaneous builds at the same exact second (in which case the destroot would happen normally), or a second apart (thus triggering the rebuild in the destroot phase, and, in my case, an error message from UsingTheRightCompiler).

comment:2 Changed 6 years ago by ryandesign (Ryan Schmidt)

  • Resolution set to fixed
  • Status changed from new to closed

As shipped, the makefile does nothing in the all rule, other than depend on TARG. So I fixed the problem in r87788 by adding a step to the all rule to change the modification time of the object and library directories to an earlier time, namely the time the makefile was modified (it seemed as good a time as any). That way, by the time the install rule is called, the library directory will be older than the libraries, so a rebuild won't occur. I have never seen a makefile do this, and I know plenty of makefiles that create directories, so I feel like there must be an easier or better solution, but I don't know it. I have a feeling it might involve not using rules to represent directory creations, but changing that is more re-engineering than I wanted to get into right now.

Note: See TracTickets for help on using tickets.