I am wondering is there a way to refactor these 3 lines without changing the logic?
scalac -cp ${SCALA_FLAGS} GrammarUtil.scala
scalac -cp ${SCALA_FLAGS} FirstSpec.scala
scalac -cp ${SCALA_FLAGS} FollowSpec.scala
Makefile
SPECS=FirstSpec FollowSpec
EXAMPLES_PATH=../..
ROOT_PATH=../${EXAMPLES_PATH}
SCALAV=2.12
APSLIB=${ROOT_PATH}/lib/aps-library-${SCALAV}.jar
SCALA_FLAGS=.:${APSLIB}
APS2SCALA=${ROOT_PATH}/bin/aps2scala
.PHONY: all
all: $(addsuffix .run, $(SPECS))
.PHONY: clean
clean:
rm -f *.class grammar.scala first.scala follow.scala
# for anything.scala it compiles it to create class file
%.class: %.scala
scalac -cp ${SCALA_FLAGS} $<
# for anything.scala that does not already exist, it generates it
%.scala:
${APS2SCALA} -DCOT -p ${EXAMPLES_PATH}:${ROOT_PATH}/base $*
# for anything.run it needs to be compiled first before running
%.run: %.class
scala -cp ${SCALA_FLAGS} $(basename $<)
# for GrammarUtil.class it needs to generate and compile grammar.class and then compile itself
GrammarUtil.class: grammar.class
scalac -cp ${SCALA_FLAGS} GrammarUtil.scala
# How to avoid above line: scalac -cp ${SCALA_FLAGS} GrammarUtil.scala
# for FirstSpec.class it needs to compile Spec.class, GrammarUtil.class and compile and then generate first.class
FirstSpec.class: Spec.class GrammarUtil.class first.class
scalac -cp ${SCALA_FLAGS} FirstSpec.scala
# How to avoid above line: scalac -cp ${SCALA_FLAGS} FirstSpec.scala
# for FollowSpec.class it needs to compile Spec.class, GrammarUtil.class and compile and then generate follow.class
FollowSpec.class: Spec.class GrammarUtil.class follow.class
scalac -cp ${SCALA_FLAGS} FollowSpec.scala
# How to avoid above line: scalac -cp ${SCALA_FLAGS} FollowSpec.scala
I hope, that the answer could look like that (put it to some clean directory and just run make, then look at those files with text editor/wiever):
SPECS=FirstSpec FollowSpec
EXAMPLES_PATH=../..
ROOT_PATH=../${EXAMPLES_PATH}
SCALAV=2.12
APSLIB=${ROOT_PATH}/lib/aps-library-${SCALAV}.jar
SCALA_FLAGS=.:${APSLIB}
APS2SCALA=${ROOT_PATH}/bin/aps2scala
.PHONY: all
all: $(addsuffix .run, $(SPECS))
.PHONY: clean
clean:
rm -f *.class grammar.scala first.scala follow.scala
# for anything.scala it compiles it to create class file
# Lets use ALL prequisities ($^) and create target by name (--output $#)
%.class: %.scala
echo "scalac -cp ${SCALA_FLAGS} $^ --output $#" > $#
# for anything.scala that does not already exist, it generates it
%.scala:
echo "${APS2SCALA} -DCOT -p ${EXAMPLES_PATH}:${ROOT_PATH}/base $*" >$#
# for anything.run it needs to be compiled first before running
%.run: %.class
echo "scala -cp ${SCALA_FLAGS} $(basename $<)" >$#
# for GrammarUtil.class it needs to generate and compile grammar.class and then compile itself
# for FirstSpec.class it needs to compile Spec.class, GrammarUtil.class and compile and then generate first.class
# for FollowSpec.class it needs to compile Spec.class, GrammarUtil.class and compile and then generate follow.class
GrammarUtil.class: grammar.class
FirstSpec.class: Spec.class GrammarUtil.class first.class
FollowSpec.class: Spec.class GrammarUtil.class follow.class
I have no scala, so I could not try it, but I let it put commands to create each file to that file for inspection - so it may be enought just to delete those echo " and " >$# to get the desired result.
Also I hope, that the scalacp command have some way to place output to explicitly named file - I used hypotetical --output filename switch for that. Or that is somehow get it from its first file parametr right (so the --outpu filename is not needed)
There are used 3 "tricks"
to use all from dependencies there is $^ variable
to get name of target file is variable $#
there can be more dependencies lines (like those 3 at bottom) and they use the same rule for the target, if it meets the same pattern
Related
I am new to Makefiles and it works as expected. But I am wondering is there a way to simplify this Makefile I wrote. More specifically I used $(basename $#).scala and $(basename $#) in many places and I think it is an anti-pattern. Or maybe is there a way to generalize it.
I appreciate any feedback or hint.
SPECS=First Follow
EXAMPLES_PATH=../..
ROOT_PATH=../${EXAMPLES_PATH}
SCALAV=2.12
APSLIB=${ROOT_PATH}/lib/aps-library-${SCALAV}.jar
SCALA_FLAGS=.:${APSLIB}
APS2SCALA=${ROOT_PATH}/bin/aps2scala
all: $(addsuffix Spec.compile, $(SPECS)) $(addsuffix Spec.run, $(SPECS))
%.generate:
${APS2SCALA} -DCOT -p ${EXAMPLES_PATH}:${ROOT_PATH}/base $*
%.run:
#scala -cp ${SCALA_FLAGS} $(basename $#)
GrammarUtil.compile: grammar.generate
scalac -cp ${SCALA_FLAGS} grammar.scala $(basename $#).scala
first.compile:
scalac -cp ${SCALA_FLAGS} $(basename $#).scala
follow.compile:
scalac -cp ${SCALA_FLAGS} $(basename $#).scala
Spec.compile:
scalac -cp ${SCALA_FLAGS} $(basename $#).scala
FirstSpec.compile: Spec.compile grammar.generate GrammarUtil.compile first.generate first.compile
scalac -cp ${SCALA_FLAGS} $(basename $#).scala
FollowSpec.compile: Spec.compile grammar.generate GrammarUtil.compile follow.generate follow.compile
scalac -cp ${SCALA_FLAGS} $(basename $#).scala
clean:
rm -f *.class grammar.scala first.scala follow.scala
The Make variable $* does roughly this; it returns whatever matched % in a pattern rule (called the stem).
Several of your recipes have exactly the same code, so there are multiple reasons to refactor to use a pattern rule instead. A complication as that one of the *.compile rules has a second dependency, but we can simply create a rule which leaves this part empty if there is only one dependency.
%.compile: %.scala
scalac -cp ${SCALA_FLAGS} $(filter-out $<,$^) $<
grammar.compile: grammar.generate
This also fixes the bug that it declares the formerly undeclared (but obvious) dependency on the %.scala file; thus, we use $< instead of $*.scala though in this case they are basically equivalent.
$(filter-out $<,$^) removes the first dependency (%.scala) from the list of all depencencies, yielding either nothing or grammar.generate.
However, your Makefile still appears to have several other undeclared dependencies, and inconsistent naming for some targets.
In particular, the above requires you to rename GrammarUtil to grammar; without more information about your inputs and their internal dependencies, I can't tell if that's really feasible.
Probably a less rocky road would be to keep exactly the same stem for all related files, and accept the default output file name (I'm guessing %.o? But I'm not familiar with Scala) instead of made-up and probably nonexistent pseudo-filenames like %.compile. (If you want to keep these, and they indeed do not correspond to actual file names, you should probably declare them as .PHONY; but then you are disabling useful and important make functionality.)
I've serious problems trying to compile this makefile inside a AIX environment.
The folder structure is as follows (using the command provided in one of the answers here: Linux command to print directory structure in the form of a tree find . | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/" ):
|-src
| |-main.c
|-obj
|-bin
|-Makefile
|-main.c
I'm trying to compile this structure using this makefile:
.SUFFIXES:
.DEFAULT:
CC = xlc_r
CFLAGSD = -g
CFLAGS = -Wall
CDEF =
LOADOPTS =
BIN_DIR = bin/
OBJ_DIR = obj/
SRC_DIR = src/
LIB_DIR = lib/
INC_DIR = include/
INC_BASE = -I./$(INC_DIR)
INC_SOURCE = -I./$(SRC_DIR)
INC_ALL = $(INC_BASE) $(INC_SOURCE)
# Libraries packed in macros
LIB_BASE = -L./$(LIB_DIR)
LIB_MATH = -lm
LIB_ALL = -lrt $(LIB_BASE)
# Libraries needed by the different programs and their respective names
LIB_PROGRAM_01 = $(LIB_ALL)
PROGRAM_NAME_01 = main
.PHONY: all
all: $(PROGRAM_NAME_01) \
execute \
$(OBJ_DIR)%.o: $(SRC_DIR)%.c
# --- Compiling $< ---
# $(CC) $(CFLAGSD) $(INC_ALL) -c $(SRC_DIR)$< -o $(OBJ_DIR)$#
create_directories:
if [ ! -d ./$(BIN_DIR) ]; then mkdir -p ./$(BIN_DIR);fi
if [ ! -d ./$(OBJ_DIR) ]; then mkdir -p ./$(OBJ_DIR);fi
execute: $(PROGRAM_NAME_01)
# --- Execute $< ---
# ./$(BIN_DIR)$<
$(PROGRAM_NAME_01): $(OBJ_DIR)main.o
# --- Linking $< ---
# $(CC) $(CDEF) $^ -o $(BIN_DIR)$# $(INC_ALL) $(LIB_PROGRAM_01)
cleanobj:
# --- Clean all objects ---
# rm -f $(OBJ_DIR)*.o
clean:
# --- Clean all objects and the bin dir too ---
# rm -f $(OBJ_DIR)*.o $(BIN_DIR)*
allc: all \
cleanobj
Debug: all
Release: all
default: all
The error I obtain is the next:
make: 1254-002 Cannot find a rule to create target obj/main.o from dependencies.
But, if I try to compile in Cygwin or Debian, it works.
I tried to call make -r -d to see all the internals and debugging simbols, but it never refers to anything close remotely to the src/main.c file.
With -d, the makefile shows the follows:
#
# Files that are only sources:
# src/%.c [src/%.c]
# obj/main.o [obj/main.o]
#*** Internal (default) Variables:
#*** Global Variables:
The version of make in aix is: 1.1.1.3 using the command grep bos /usr/ccs/lib/aix.mk
Your makefile is a GNU Make makefile but your AIX make program is not GNU Make
and will not understand many GNU Make makefiles. Your make program in Cygwin or Debian is GNU Make.
Install and use GNU Make.
Whenever I try to run this, the only output I get is "make: foo.o is up to date." It seems as if the rest of the program does not run and I do not know why. My instructions are as follows: "Compile a C program. Run a C program. Run a Python program. Compile and run a java program. Check for a README, display it. Compare 2 files. Clean up intermediary files."
cc = gcc
EXE = foo
JAVAC = javac
JRE = java
PAGER = less
TEST_OUT = test.out
EXP_OUT = expected.out
foo.o: foo.c foo.h
$(cc) -c foo.c
main.o: main.c foo.h
$(cc) -c main.c
$(EXE): foo.o main.o
$(cc) -o$(EXE) main.o foo.o
run-c: $(EXE)
./$(EXE)
run-py:
./foo.py
read: README
$(PAGER)
foo.class: foo.java
$(JAVAC) foo.java
run-java: foo.cass
$(JRE) foo
save-java:
./(run-java) >> $(TEST_OUT)
test-java: $(TEST_OUT) $(EXP_OUT)
#if diff $(TEST_OUT) $(EXP_OUT) &> /dev/null ; then \
echo "Passed!" ;\
else \
echo "Not the same!" ;\
fi
clean:
-rm test.out
Whenever I try to run this, the only output I get is "make: foo.o is up to date."
By default, make runs the topmost rule when no target is specified. You have to run for example make run-c to invoke a corresponding recipe, or you can just put an all rule before any others which depends on and does all the things.
read: README
$(PAGER)
I suspect you might have missed putting $# after $(PAGER) as the argument.
save-java:
./(run-java) >> $(TEST_OUT)
You can't just "include" other recipes this way. Instead, repeat what's in run-java and append the redirection.
If you want to specify "pseudo" targets, I recommend you to specify them as .PHONY, such as:
.PHONY: all run-c run-py run-java save-java test-java clean
To mark some targets as intermediate files, use the .INTERMEDIATE directive. GNU Make manual (texinfo) is available both online and via the info command.
I've replaced my many .sh files from my previous question with a makefile, of sorts. I don't know much about makefiles, admittedly, but this seems to work:
MainPackage=jnitest
Main=SimpleJNITest
cFileName=rtm_simple
targetDir=classes
libDir=lib
srcDir=src
jdkDir="/home/user/local/java/jdk1.7.0_65"
java="$(jdkDir)/bin/java"
javah="$(jdkDir)/bin/javah"
javac="$(jdkDir)/bin/javac"
JAVAC_FLAGS=-d "$(targetDir)" -sourcepath "$(srcDir)" -cp "$(targetDir):$(libDir)/*"
JAVAH_FLAGS=-d "$(ccodeDir)" -cp "$(targetDir)" -jni
JAVA_FLAGS=-Djava.library.path="$(LD_LIBRARY_PATH):$(libDir)" -cp "$(targetDir):$(libDir)/*"
ccodeDir=$(srcDir)/ccode
CC=gcc
CC_FLAGS=-g -shared -fpic -I "$(jdkDir)/include" -I "$(jdkDir)/include/linux"
cFile="$(ccodeDir)/$(cFileName).c"
soFile="$(libDir)/lib$(cFileName).so"
dirs:
mkdir -p "$(targetDir)"
mkdir -p "$(libDir)"
java: dirs
$(javac) $(JAVAC_FLAGS) "$(srcDir)/$(MainPackage)/$(Main).java"
header:
$(javah) $(JAVAH_FLAGS) "$(MainPackage).$(Main)"
c:
$(CC) $(CC_FLAGS) $(cFile) -o $(soFile)
all:
$(MAKE) java
$(MAKE) header
$(MAKE) c
run:
$(java) $(JAVA_FLAGS) "$(MainPackage).$(Main)"
clean:
rm -rf classes
rm -rf lib
rm -f $(ccodeDir)/$(MainPackage)_$(Main).h
My problem, now, lies with MainPackage=jnitest.
So long as MainPackage is a single word, everything is fine.
However, when it is not, I'll be needing it once in slash notation for
$(javac) $(JAVAC_FLAGS) "$(srcDir)/$(MainPackage)/$(Main).java"
and once in dot notation for
$(java) $(JAVA_FLAGS) "$(MainPackage).$(Main)"
In bash, you could do something like
MainPackage_slashed=$(echo "$MainPackage" | tr '.' '/')
How do I insert one such transformation into a makefile?
You're looking for the subst function, see the GNU make manual.
Example:
foo=x.y.z
bar=$(subst .,/,$(foo))
$(info $(bar))
prints x/y/z.
You will need to use shell function in your Makefile like this:
MainPackage_slashed := $(shell echo "$(MainPackage)" | tr '.' '/')
I am trying to compile a .ttcn file with my a generated makefile with role is to create a .cc and .hh file.
But when I run the make command I have a problem with the .o file, it is not generated so my .exe is not generated too
here is the error I have and my Makefile:
Makefile:
# This Makefile was generated by the Makefile Generator
# of the TTCN-3 Test Executor version CRL 113 200/5 R2A
# for U-TMV\qi11091 (qi11091#WMUC-0118) on Thu Apr 9 16:49:49 2015
# Copyright Ericsson Telecom AB 2000-2014
# The following make commands are available:
# - make, make all Builds the executable test suite.
# - make archive Archives all source files.
# - make check Checks the semantics of TTCN-3 and ASN.1 modules.
# - make clean Removes all generated files.
# - make compile Translates TTCN-3 and ASN.1 modules to C++.
# - make dep Creates/updates dependency list.
# - make executable Builds the executable test suite.
# - make library Builds the library archive.
# - make objects Builds the object files without linking the executable.
# WARNING! This Makefile can be used with GNU make only.
# Other versions of make may report syntax errors in it.
#
# Do NOT touch this line...
#
.PHONY: all shared_objects executable library objects check clean dep archive
.SUFFIXES: .d
#
# Set these variables...
#
# The path of your TTCN-3 Test Executor installation:
# Uncomment this line to override the environment variable.
# TTCN3_DIR =
# Your platform: (SOLARIS, SOLARIS8, LINUX, FREEBSD or WIN32)
PLATFORM = WIN32
# Your C++ compiler:
# (if you change the platform, you may need to change the compiler)
CXX = g++
# Flags for the C++ preprocessor (and makedepend as well):
CPPFLAGS = -D$(PLATFORM) -I$(TTCN3_DIR)/include
# Flags for dependency generation
CXXDEPFLAGS = -MM
# Flags for the C++ compiler:
CXXFLAGS = -Wall
# Flags for the linker:
LDFLAGS =
ifeq ($(PLATFORM), WIN32)
# Silence linker warnings.
LDFLAGS += -Wl,--enable-auto-import,--enable-runtime-pseudo-reloc
endif
# Utility to create library files
AR = ar
ARFLAGS =
# Flags for the TTCN-3 and ASN.1 compiler:
COMPILER_FLAGS = -L
# Execution mode: (either ttcn3 or ttcn3-parallel)
TTCN3_LIB = ttcn3-parallel
# The path of your libxml2 installation:
# If you do not have your own one, leave it unchanged.
XMLDIR = $(TTCN3_DIR)
# Directory to store the archived source files:
ARCHIVE_DIR = backup
#
# You may change these variables. Add your files if necessary...
#
# TTCN-3 modules of this project:
TTCN3_MODULES = TTCN3SessionSetup.ttcn
# ASN.1 modules of this project:
ASN1_MODULES =
# C++ source & header files generated from the TTCN-3 & ASN.1 modules of
# this project:
GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) $(ASN1_MODULES:.asn=.cc)
GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh)
# C/C++ Source & header files of Test Ports, external functions and
# other modules:
USER_SOURCES =
USER_HEADERS = $(USER_SOURCES:.cc=.hh)
# Object files of this project that are needed for the executable test suite:
OBJECTS = $(GENERATED_OBJECTS) $(USER_OBJECTS)
GENERATED_OBJECTS = $(GENERATED_SOURCES:.cc=.o)
USER_OBJECTS = $(USER_SOURCES:.cc=.o)
DEPFILES = $(USER_OBJECTS:.o=.d) $(GENERATED_OBJECTS:.o=.d)
# Other files of the project (Makefile, configuration files, etc.)
# that will be added to the archived source files:
OTHER_FILES = Makefile
# The name of the executable test suite:
EXECUTABLE = TTCN3SessionSetup.exe
LIBRARY = libTTCN3SessionSetup.a
TARGET = $(EXECUTABLE)
#
# Do not modify these unless you know what you are doing...
# Platform specific additional libraries:
#
SOLARIS_LIBS = -lsocket -lnsl -lxml2
SOLARIS8_LIBS = -lsocket -lnsl -lxml2
LINUX_LIBS = -lxml2
FREEBSD_LIBS = -lxml2
WIN32_LIBS = -lxml2
#
# Rules for building the executable...
#
all: $(TARGET) ;
executable: $(EXECUTABLE) ;
library: $(LIBRARY) ;
objects: $(OBJECTS) compile;
$(EXECUTABLE): $(OBJECTS)
if $(CXX) $(LDFLAGS) -o $# $^ \
-L$(TTCN3_DIR)/lib -l$(TTCN3_LIB) \
-L$(OPENSSL_DIR)/lib -lcrypto \
-L$(XMLDIR)/lib $($(PLATFORM)_LIBS); \
then : ; else $(TTCN3_DIR)/bin/titanver $(OBJECTS); exit 1; fi
$(LIBRARY): $(OBJECTS)
$(AR) -r $(ARFLAGS) $(LIBRARY) $(OBJECTS)
.cc.o .c.o:
$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) -o $# $<
.cc.d .c.d:
#echo Creating dependency file for '$<'; set -e; \
$(CXX) $(CXXDEPFLAGS) $(CPPFLAGS) $(CXXFLAGS) $< \
| sed 's/\($*\)\.o[ :]*/\1.o $# : /g' > $#; \
[ -s $# ] || rm -f $#
$(GENERATED_SOURCES) $(GENERATED_HEADERS): compile
#if [ ! -f $# ]; then $(RM) compile; $(MAKE) compile; fi
check: $(TTCN3_MODULES) $(ASN1_MODULES)
$(TTCN3_DIR)/bin/compiler -s $(COMPILER_FLAGS) $^
compile: $(TTCN3_MODULES) $(ASN1_MODULES)
$(TTCN3_DIR)/bin/compiler $(COMPILER_FLAGS) $^ - $?
touch $#
clean:
-$(RM) $(EXECUTABLE) $(LIBRARY) $(OBJECTS) $(GENERATED_HEADERS) \
$(GENERATED_SOURCES) compile $(DEPFILES) \
tags *.log
dep: $(GENERATED_SOURCES) $(USER_SOURCES) ;
ifeq ($(findstring n,$(MAKEFLAGS)),)
ifeq ($(filter clean check compile archive diag,$(MAKECMDGOALS)),)
-include $(DEPFILES)
endif
endif
archive:
mkdir -p $(ARCHIVE_DIR)
tar -cvhf - $(TTCN3_MODULES) $(ASN1_MODULES) \
$(USER_HEADERS) $(USER_SOURCES) $(OTHER_FILES) \
| gzip >$(ARCHIVE_DIR)/`basename $(TARGET) .exe`-`date '+%y%m%d-%H%M'`.tgz
diag:
$(TTCN3_DIR)/bin/compiler -v 2>&1
$(TTCN3_DIR)/bin/mctr_cli -v 2>&1
$(CXX) -v 2>&1
$(AR) -V 2>&1
#echo TTCN3_DIR=$(TTCN3_DIR)
#echo OPENSSL_DIR=$(OPENSSL_DIR)
#echo XMLDIR=$(XMLDIR)
#echo PLATFORM=$(PLATFORM)
#
# Add your rules here if necessary...
#
And here is my error
qi11091#WMUC-0118 ~/Documents/TITAN_Command_Line/FirstTest
$ make
/cygdrive/c/Users/qi11091/Documents/TITAN_files/TITAN/bin/compiler -L TTCN3Ses sionSetup.ttcn - TTCN3SessionSetup.ttcn
Notify: Parsing TTCN-3 module `TTCN3SessionSetup.ttcn'...
Notify: Checking modules...
Notify: Generating code...
Notify: File `TTCN3SessionSetup.hh' was generated.
Notify: File `TTCN3SessionSetup.cc' was generated.
Notify: 2 files were updated.
touch compile
Creating dependency file for TTCN3SessionSetup.cc
g++: Fehler: spawn: No such file or directory
g++ -c -DWIN32 -I/cygdrive/c/Users/qi11091/Documents/TITAN_files/TITAN /include -Wall -o TTCN3SessionSetup.o TTCN3SessionSetup.cc
g++: Fehler: spawn: No such file or directory
Makefile:151: die Regel für Ziel „TTCN3SessionSetup.o“ scheiterte
make: *** [TTCN3SessionSetup.o] Fehler 1
If that's an exact copy of the output you're seeing, then one problem is:
g++ -c -DWIN32 -I/cygdrive/c/Users/qi11091/Documents/TITAN_files/TITAN /include -Wall -o TTCN3SessionSetup.o TTCN3SessionSetup.cc
Note all the spaces between .../TITAN_files/TITAN and /include. That whitespace makes the compiler think these are separate arguments, and it seems like you're trying to add the "file" /include to your compile line, which does not exist of course.
Your makefile doesn't actually set the TTCN3_DIR variable, in this example, so I don't know where that value is coming from but wherever it is, you need to be sure there is no whitespace at the end of the value.