views:

197

answers:

2

I have a makefile:

#Nice, wonderful makefile written by Jason
CC=g++
CFLAGS=-c -Wall
BASE_DIR:=.
SOURCE_DIR:=$(BASE_DIR)/source
BUILD_DIR:=$(BASE_DIR)/build
TEST_DIR:=$(BASE_DIR)/build/tests
MAKEFILE_DIR:=$(BASE_DIR)/makefiles
DATA_DIR:=$(BASE_DIR)/data
DATA_DIR_TESTS:=$(DATA_DIR)/tests
MOLECULE_UT_SOURCES :=  $(SOURCE_DIR)/molecule_test/main.cc \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager.cpp \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_manager_main.cpp \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader.cpp \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.cpp \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager.cpp \
    $(SOURCE_DIR)/parser.h \
    $(SOURCE_DIR)/parser.cpp \
    $(SOURCE_DIR)/common.h
MOLECULE_UT_DATA := \
    $(DATA_DIR_TESTS)/molecule_test/par_oxalate_and_friends.inp \
    $(DATA_DIR_TESTS)/molecule_test/dicarboxy-octane_4.pdb \
    $(DATA_DIR_TESTS)/molecule_test/dicarboxy-octane_4.psf
PARAM_UT_SOURCES :=  $(SOURCE_DIR)/parameter_test/main.cc \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager.cpp \
    $(SOURCE_DIR)/parser.h \
    $(SOURCE_DIR)/parser.cpp \
    $(SOURCE_DIR)/common.h
PARAM_UT_DATA := $(DATA_DIR_TESTS)/molecule_test/par_oxalate_and_friends.inp

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
    molecule_test_prepare_data_files
    @$(shell cd $(TEST_DIR)/molecule_unit_test/; \
    make ./bin/molecule_test)

molecule_test_prepare_sources: molecule_test_dir
    @echo Copying sources...
    @cp --preserve $(MOLECULE_UT_SOURCES) \
    $(TEST_DIR)/molecule_unit_test/source

molecule_test_prepare_makefiles: $(MAKEFILE_DIR)/Makefile.molecule_test
    @cp  --preserve $(MAKEFILE_DIR)/Makefile.molecule_test \
    $(TEST_DIR)/molecule_unit_test/Makefile

molecule_test_prepare_data_files:
    cp --preserve $(MOLECULE_UT_DATA) $(TEST_DIR)/molecule_unit_test/bin/

molecule_test_dir:
    @if test -d $(BUILD_DIR); then \
        echo Build exists...; \
        else \
        echo Build directory does not exist, making build dir...; \
    mkdir $(BUILD_DIR); \
        fi
    @if test -d $(TEST_DIR); then \
        echo Tests exists...; \
        else \
        echo Tests directory does not exist, making tests dir...; \
    mkdir $(TEST_DIR); \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test; then \
        echo Molecule unit test directory exists...; \
        else \
        echo Molecule unit test directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/source; then \
        echo Molecule unit test source directory exists...; \
        else \
        echo Molecule unit test source directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/source; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/obj; then \
        echo Molecule unit test object directory exists...; \
        else \
        echo Molecule unit test object directory does \
        not exist, making object dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/obj; \
        fi
    @if test -d $(TEST_DIR)/molecule_unit_test/bin; then \
        echo Molecule unit test executable directory exists...; \
        else \
        echo Molecule unit test executable directory does \
        not exist, making executable dir...; \
        mkdir $(TEST_DIR)/molecule_unit_test/bin; \
        fi

param_test : param_test_prepare_sources param_test_prepare_makefiles \
    param_test_prepare_data_files
    @$(shell cd $(TEST_DIR)/param_unit_test/; \
    make ./bin/param_test)

param_test_prepare_sources: param_test_dir
    @echo Copying sources...
    @cp --preserve $(PARAM_UT_SOURCES) $(TEST_DIR)/param_unit_test/source

param_test_prepare_makefiles: $(MAKEFILE_DIR)/Makefile.param_test
    @cp  --preserve $(MAKEFILE_DIR)/Makefile.param_test \
    $(TEST_DIR)/param_unit_test/Makefile

param_test_prepare_data_files:
    cp --preserve $(PARAM_UT_DATA) $(TEST_DIR)/param_unit_test/bin/

param_test_dir:
    @if test -d $(BUILD_DIR); then \
        echo Build exists...; \
        else \
        echo Build directory does not exist, making build dir...; \
    mkdir $(BUILD_DIR); \
        fi
    @if test -d $(TEST_DIR); then \
        echo Tests exists...; \
        else \
        echo Tests directory does not exist, making tests dir...; \
    mkdir $(TEST_DIR); \
        fi
    @if test -d $(TEST_DIR)/param_unit_test; then \
        echo Param unit test directory exists...; \
        else \
        echo Param unit test directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/param_unit_test; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/source; then \
        echo Param unit test source directory exists...; \
        else \
        echo Param unit test source directory does \
        not exist, making build dir...; \
        mkdir $(TEST_DIR)/param_unit_test/source; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/obj; then \
        echo Param unit test object directory exists...; \
        else \
        echo Param unit test object directory does \
        not exist, making object dir...; \
        mkdir $(TEST_DIR)/param_unit_test/obj; \
        fi
    @if test -d $(TEST_DIR)/param_unit_test/bin; then \
        echo Param unit test executable directory exists...; \
        else \
        echo Param unit test executable directory does \
        not exist, making executable dir...; \
        mkdir $(TEST_DIR)/param_unit_test/bin; \
        fi

That calls a second makefile after it creates and populates the directory structure.

The second makefile is as follows:

#Nice, wonderful makefile written by Jason
CC=g++
CFLAGS=-c -Wall
SOURCE_DIR:=./source
OBJ_DIR:=./obj
EXE_DIR:=./bin

$(EXE_DIR)/molecule_test : $(OBJ_DIR)/main.o \
    $(OBJ_DIR)/parameter_manager_lj_molecule.o \
    $(OBJ_DIR)/parameter_manager.o $(OBJ_DIR)/parser.o \
    $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
    $(OBJ_DIR)/molecule_reader.o \
    $(OBJ_DIR)/molecule_reader_psf_pdb.o
    @$(CC) $(OBJ_DIR)/main.o $(OBJ_DIR)/parameter_manager.o \
    $(OBJ_DIR)/parser.o $(OBJ_DIR)/parameter_manager_lj_molecule.o \
    $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
    $(OBJ_DIR)/molecule_reader.o \
    $(OBJ_DIR)/molecule_reader_psf_pdb.o \
    -o molecule_test
    @mv molecule_test $(EXE_DIR)/ 

$(OBJ_DIR)/main.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/common.h $(SOURCE_DIR)/main.cc
    $(CC) $(CFLAGS) $(SOURCE_DIR)/main.cc
    @mv main.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_reader.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_reader.cpp
    @mv molecule_reader.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_reader_psf_pdb.o: $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parameter_manager_lj_molecule.h \
    $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/molecule_reader.h \
    $(SOURCE_DIR)/molecule_reader_psf_pdb.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_reader_psf_pdb.cpp
    @mv molecule_reader_psf_pdb.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_manager.o: $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_manager.cpp
    @mv molecule_manager.o $(OBJ_DIR)/

$(OBJ_DIR)/molecule_manager_main.o: $(SOURCE_DIR)/molecule_manager.h \
    $(SOURCE_DIR)/molecule_manager_main.h \
    $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/molecule_manager_main.cpp
    @mv molecule_manager_main.o $(OBJ_DIR)/

$(OBJ_DIR)/parameter_manager_lj_molecule.o: $(SOURCE_DIR)/common.h \
    $(SOURCE_DIR)/parameter_manager.h \
    $(SOURCE_DIR)/parser.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/parameter_manager_lj_molecule.cpp
    @mv parameter_manager_lj_molecule.o $(OBJ_DIR)/

$(OBJ_DIR)/parameter_manager.o: $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/parameter_manager.cpp
    @mv parameter_manager.o $(OBJ_DIR)/

$(OBJ_DIR)/parser.o: $(SOURCE_DIR)/parser.h
    @$(CC) $(CFLAGS) $(SOURCE_DIR)/parser.cpp
    @mv parser.o $(OBJ_DIR)/

$(OBJ_DIR)/common.o: $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) $(SOURCE_DIR)/common.h
    mv common.h.gch $(OBJ_DIR)/

I admit I'm somewhat of a novice at Makefiles. I would both like advice as to how to streamline these files (without too much "magic") and how to fix these two errors...

First I have to say everything is working, so to speak. When I build my target it creates all the directories right and generates the executable. And all my files get copied properly and get recompiled when I touch the files in my base-level source directory. So these aren't "real" errors so to speak, just annoying error text I want to get rid of...

The first error occurs when I run a build make molecule_test which requires it to do something. Whatever it needs to do gets done, but I also get:

g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
g++: g++: No such file or directory
make: *** [molecule_test] Error 1

..AGAIN the build succeeds, creating the executable properly

The second error I get occurs when there's nothing to be done...when that happens I get:

/bin/sh: -c: line 0: unexpected EOF while looking for matching ``'
/bin/sh: -c: line 1: syntax error: unexpected end of file

Please be gentle... I've read basic makefile tutorials, including the gnu makefile tutorial, but there seems to be a leap between creating a small program with a handful of local sources and a large program with the need for nested directories, data files, etc. I'm trying to make that leap... unfortunately I have no best practice makefiles from past code as I'm at a small research group at a university, not a corporate atmosphere.

My basic approach is to create a base directory with the following

[dir] source/
[dir] data/
[dir] makefiles/
[dir] build/    **gets created
Makefile

The top level makefile creates a subdirectory in the build directory, copies the needed sources (say for a particular test program, and needed data files, and a makefile to make all the sources. The top level makefile then calls the build-level makefile.

I'd be open to ideas on how to streamline this process, but would appreciate if we FIRST resolve the errors.

Thanks in advance!!!

P.S. I'm running on Centos 5.4, GNU Make 3.81, gcc version 4.1.2 20080704 (Red Hat 4.1.2-44) .... GNU Make and gcc are both 64-bit versions...

A: 

For a start you can get rid of all the mv commands and use make's built-in variables, e.g.

$(OBJ_DIR)/parameter_manager.o: $(SOURCE_DIR)/parameter_manager.cpp $(SOURCE_DIR)/common.h
    $(CC) $(CFLAGS) -o $@ $<
Paul R
How does the `$@` and `$<` work... will these drop the objects in my object folder (where my final build is performed)? I'd like to keep them separate...
Jason R. Mick
@Jason R. Mick: they are Automatic Variables (see the gnu make manual). `$@` expands to the target, and `$<` expands to the first prerequisite.
Beta
Ahh. Quick question; I work in a lab where most people know much less than coding than me, so maintaining this project may be difficult to say the least. I'm afraid putting nice tricks like that in will worsen that situation. I'm not sure whether I should be trying to write my code/makefiles for readability for novices or for optimal grammar, so to speak. Suggestions??Also does NO ONE have an idea about my original make system errors?????
Jason R. Mick
@Jason: ideally you want to simplify your makefile, which may make it a little more cryptic but this will hopefully be outweighed by ease of maintenance - look at default rules in the GNU make manual, using the `%` wildcard. You should be able to set up some default rules and then your less enlightened colleagues just have to update the dependencies, which should be pretty easy and doesn't require understanding the more cryptic parts of the makefile.
Paul R
Thanks. Paul. Still no answers on the above errors, though?? ANYONE?
Jason R. Mick
@Jason: all I can suggest is that you use a "divide and conquer" approach to debugging your makefile - reduce it down to a minimal makefile that just compiles one source file and get that working, then build it up in stages to the full version.
Paul R
+1  A: 

This fix seems to clear up both of your errors:

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
  molecule_test_prepare_data_files
    @cd $(TEST_DIR)/molecule_unit_test && $(MAKE) ./bin/molecule_test

The $(shell ...) command is for invoking a shell outside of a rule. There's no need to use it here, since this is a command in a rule-- it's already happening in a subshell. Also note that this uses $(MAKE) instead of make (the reasons are a little subtle, just think of it as a good habit).

You can do it even more concisely and quietly:

molecule_test : molecule_test_prepare_sources molecule_test_prepare_makefiles \
  molecule_test_prepare_data_files
    @$(MAKE) -s -C $(TEST_DIR)/molecule_unit_test ./bin/molecule_test

As for streamlining, there's a lot you can do. You can reduce the length of your second makefile by about half, and fix what appear to be a number of bugs, and with the first one you can do even better. It depends on how much "magic" you can tolerate. Here's a quick attempt at streamlining your second Makefile (since I don't have your files to test it with I can't promise it'll work without some touchups).

CC=g++
CFLAGS=-c -Wall
SOURCE_DIR:=./source
INCDIRS := -I$(SOURCE_DIR)
OBJ_DIR:=./obj
EXE_DIR:=./bin

VPATH = $(SOURCE_DIR)

$(EXE_DIR)/molecule_test : $(OBJ_DIR)/main.o \
  $(OBJ_DIR)/parameter_manager_lj_molecule.o \
  $(OBJ_DIR)/parameter_manager.o $(OBJ_DIR)/parser.o \
  $(OBJ_DIR)/molecule_manager.o $(OBJ_DIR)/molecule_manager_main.o \
  $(OBJ_DIR)/molecule_reader.o \
  $(OBJ_DIR)/molecule_reader_psf_pdb.o
    @$(CC) $^ -o $@

$(OBJ_DIR)/main.o $(OBJ_DIR)/molecule_reader.o \
  $(OBJ_DIR)/molecule_reader_psf_pdb.o: \
  molecule_manager.h \
  molecule_manager_main.h \
  parameter_manager.h \
  parameter_manager_lj_molecule.h

$(OBJ_DIR)/main.o: main.cpp \
  molecule_reader.h \
  molecule_reader_psf_pdb.h common.h
    $(CC) $(CFLAGS) $(INCDIRS) $< $@

$(OBJ_DIR)/molecule_reader_psf_pdb.o: molecule_reader.h

$(OBJ_DIR)/parameter_manager_lj_molecule.o: parser.h

%.o: %.cpp %.h common.h
    $(CC) $(CFLAGS) $(INCDIRS) $< -o $@
Beta
I think you need a `-o` before the `$@` in the last line ?
Paul R
@Paul R: you're right, thank you, fixed. And it probably has a more serious bug or two that will come out in testing.
Beta
Thank you both!! Will try this out now. :)
Jason R. Mick
hmm I'm getting the error:`make[1]: *** No rule to make target `main.cpp', needed by `obj/main.o'. Stop.`... when I try to make the second target...
Jason R. Mick
Okay fixed my previous error by changing the .cpp to .cc in the main.o target... but now I get this error...`obj/main.o: file not recognized: File format not recognized` ... I read that's due to if you have the file extension wrong... but my extension all look right now :O
Jason R. Mick
hurrah! I just needed to recompile. The stale main.o object was apparently messing me up... But yea your makefile was spot on, as you couldn't have known I would name it *.cc instead of *.cpp.
Jason R. Mick