├── .gitignore ├── README.md ├── easymake.mk └── samples ├── basics ├── .gitignore ├── add.cpp ├── math │ ├── add.cpp │ └── add.h └── test.sh ├── so ├── Makefile ├── add_test.cpp ├── math │ ├── add.cpp │ └── add.h └── so_test.sh ├── staticLib ├── Makefile ├── add_test.cpp ├── ar_test.sh └── math │ ├── add.cpp │ └── add.h └── vpath ├── proj ├── Makefile ├── main.cpp └── math │ ├── add.cpp │ └── add.h └── src2 ├── hello_world.cpp └── test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easymake 2 | 3 | ## Introduction ## 4 | 5 | Easymake is a handy makefile for C/C++ applications on Linux system. For 6 | simple applications, you don’t even need to write a single line of 7 | makefile code to build your target with easymake. 8 | 9 | Features description: 10 | 11 | - Finds and compiles all C/C++ source files in the directory recursively 12 | (optional). Places the object files and target files in a separate 13 | directory. 14 | - Only re-compiles the changed and affected source files. That is, if you 15 | modify your header `foo.h`, all your source files with `#include "foo.h"` 16 | will be re-compiled. 17 | - Supports Simple unit testing. 18 | - Handles more than one entry point in the project. 19 | - Support both [static library(libfoo.a)](samples/staticLib/Makefile) and 20 | [shared library(libfoo.so)](samples/so/Makefile) building. 21 | 22 | 23 | ***NOTICE***: Easymake is designed to be easy to use on simple applications, 24 | not as a highly flexible or extensible template. If you want more 25 | customization, you might need to look for [a small and simple 26 | example](https://gist.github.com/samuelsmal/e43f2001cfc81fee18b6) for start. 27 | 28 | ## Getting Started ## 29 | 30 | ### Basics 31 | 32 | ``` 33 | git clone https://github.com/roxma/easymake 34 | cd easymake/samples/basics 35 | cp ../../easymake.mk Makefile 36 | make 37 | ./bin/add # if you rename add.cpp to myprogram.cpp, then you get ./bin/myprogram.cpp 38 | ``` 39 | 40 | ![basics](https://cloud.githubusercontent.com/assets/4538941/24320876/fcd504c4-1179-11e7-969f-d2f2c40270e9.gif) 41 | 42 | ### Unit Testing 43 | 44 | Files with `*_test.cpp` or `*_test.c` pattern will be used for testing 45 | (inspired by golang). 46 | 47 | ![unit_test](https://cloud.githubusercontent.com/assets/4538941/24320877/fea9002a-1179-11e7-8b2c-05149689fe57.gif) 48 | 49 | ### Multi Entries 50 | 51 | ![multi_entries](https://cloud.githubusercontent.com/assets/4538941/24320879/00e48756-117a-11e7-9dcc-d14729e26dca.gif) 52 | 53 | ### Options 54 | 55 | Easymake is trying to follow the Makefile Conventions 56 | [(1)](https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html#Implicit-Variables) 57 | [(2)](https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html). 58 | The following options are supported. 59 | 60 | - `CFLAGS` Extra flags to give to the C compiler. 61 | - `CXXFLAGS` Extra flags to give to the C++ compiler. 62 | - `LDFLAGS` Extra flags to give to compilers when they are supposed to invoke the linker 63 | - `LDLIBS` Library flags or names given to compilers when they are supposed to invoke the linker 64 | - `ARFLAGS` Flags to give the archive-maintaining program; default `cr` 65 | 66 | ### Recommended Style 67 | 68 | In the GIFs, I simply copy `easymake.mk` into my souce code directory as a 69 | makefile. However, for code simplicity, I recommend the following style: 70 | 71 | ``` 72 | CXXFLAGS=-O2 73 | 74 | # other options 75 | # ... 76 | 77 | include /path/to/easymake.mk 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /easymake.mk: -------------------------------------------------------------------------------- 1 | ################################################################ 2 | # Copyright (c) 2016-2022 roxma@qq.com 3 | # https://github.com/roxma/easymake 4 | # 5 | # MIT License 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining 8 | # a copy of this software and associated documentation files (the 9 | # "Software"), to deal in the Software without restriction, including 10 | # without limitation the rights to use, copy, modify, merge, publish, 11 | # distribute, sublicense, and/or sell copies of the Software, and to 12 | # permit persons to whom the Software is furnished to do so, subject to 13 | # the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | # CFLAGS= 27 | # CXXFLAGS= 28 | # ASFLAGS= 29 | # LDFLAGS= 30 | 31 | # do not use the default `rv` option 32 | ARFLAGS=cr 33 | 34 | GEN_DEP_FLAG ?= -MP -MMD 35 | 36 | # do not use ./bin 37 | BUILD_ROOT?=bin 38 | 39 | CXXEXT?=cpp 40 | CEXT?=c 41 | # assembly files 42 | ASEXT?=S 43 | CC?=cc 44 | CXX?=c++ 45 | AR?=ar 46 | 47 | # ENTRY_LIST 48 | # VPATH= 49 | 50 | IS_TEST?=$(filter %_test.$(CEXT) %_test.$(CXXEXT),$(1)) 51 | 52 | # clears out the suffix list with implicit rules 53 | .SUFFIXES: 54 | 55 | ################################################################ 56 | 57 | ## 58 | # $(call Em_src2obj,$(CSRC) $(CXXSRC) $(ASSRC),$(BUILD_ROOT)) 59 | Em_src2obj=$(foreach _src,$(1),$(2)/$(basename $(_src)).o) 60 | 61 | # Recursive wildcard 62 | Em_rwildcard=$(foreach d,$(wildcard $1*),$(call Em_rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) 63 | 64 | # NOTICE: "./" in BUILD_ROOT may cause entry detecting problem. 65 | ifneq (,$(filter ./%,$(BUILD_ROOT))) 66 | $(error Please do not use prefix "./" in variable BUILD_ROOT=$(BUILD_ROOT)) 67 | endif 68 | 69 | # if CXXSRC are not specified, automatically scan all .$(CXXEXT) files in the 70 | # current directories. 71 | ifeq ($(strip $(CXXSRC)),) 72 | CXXSRC:=$(call Em_rwildcard,,*.$(CXXEXT)) $(foreach dir,$(VPATH),$(foreach src,$(call Em_rwildcard,$(dir),*.$(CXXEXT)),$(src:$(dir)/%=%))) 73 | CXXSRC:=$(strip $(CXXSRC)) 74 | endif 75 | ifneq (,$(findstring ..,$(CXXSRC))) 76 | $(error ".." should not appear in the cpp source list: $(CXXSRC)) 77 | endif 78 | # remove "./" in file path, which may cause pattern rules problems. 79 | CXXSRC:=$(subst ./,,$(CXXSRC)) 80 | 81 | # if CSRC are not specified, automatically scan all .$(CEXT) files in the 82 | # current directories. 83 | ifeq ($(strip $(CSRC)),) 84 | CSRC:=$(call Em_rwildcard,,*.$(CEXT)) $(foreach dir,$(VPATH),$(foreach src,$(call Em_rwildcard,$(dir),*.$(CEXT)),$(src:$(dir)/%=%))) 85 | CSRC:=$(strip $(CSRC)) 86 | endif 87 | ifneq (,$(findstring ..,$(CSRC))) 88 | $(error ".." should not appear in the c source list: $(CSRC)) 89 | endif 90 | # remove "./" in file path, which may cause pattern rules problems. 91 | CSRC:=$(subst ./,,$(CSRC)) 92 | 93 | # if ASSRC are not specified, automatically scan all .$(ASEXT) files in the 94 | # current directories. 95 | ifeq ($(strip $(ASSRC)),) 96 | ASSRC:=$(call Em_rwildcard,,*.$(ASEXT)) $(foreach dir,$(VPATH),$(foreach src,$(call Em_rwildcard,$(dir),*.$(ASEXT)),$(src:$(dir)/%=%))) 97 | ASSRC:=$(strip $(ASSRC)) 98 | endif 99 | ifneq (,$(findstring ..,$(ASSRC))) 100 | $(error ".." should not appear in the asm source list: $(ASSRC)) 101 | endif 102 | # remove "./" in file path, which may cause pattern rules problems. 103 | ASSRC:=$(subst ./,,$(ASSRC)) 104 | 105 | # if the project has c++ source, use g++ for linking instead of gcc 106 | ifneq ($(strip $(CXXSRC)),) 107 | em_linker:=$(CXX) 108 | endif 109 | em_linker?=$(CC) 110 | 111 | em_all_objects:=$(call Em_src2obj,$(CSRC) $(CXXSRC) $(ASSRC) ,$(BUILD_ROOT)) 112 | 113 | 114 | # A file that contains a list of entries detected by easymake. 115 | em_f_entries:=$(BUILD_ROOT)/em_entries 116 | 117 | # By convention, the default target should be "all" 118 | all: 119 | 120 | # If there is a main function defined in this object, register the source file 121 | # as entry. 122 | $(BUILD_ROOT)/%.o: %.$(CXXEXT) 123 | @mkdir -p $(dir $@) 124 | $(CXX) $(CXXFLAGS) $(GEN_DEP_FLAG) -c -o $@ $< 125 | @if [ $$(nm -g --format="posix" $@ | grep -c "^main T") -eq 1 ]; then \ 126 | echo "$(patsubst $(BUILD_ROOT)/%.o,%.$(CXXEXT),$@)" >> $(em_f_entries); \ 127 | sort -u $(em_f_entries) -o $(em_f_entries); \ 128 | fi; 129 | 130 | $(BUILD_ROOT)/%.o: %.$(CEXT) 131 | @mkdir -p $(dir $@) 132 | $(CC) $(CFLAGS) $(GEN_DEP_FLAG) -c -o $@ $< 133 | @if [ $$(nm -g --format="posix" $@ | grep -c "^main T") -eq 1 ]; then \ 134 | echo "$(patsubst $(BUILD_ROOT)/%.o,%.$(CEXT),$@)" >> $(em_f_entries); \ 135 | sort -u $(em_f_entries) -o $(em_f_entries); \ 136 | fi; 137 | 138 | $(BUILD_ROOT)/%.o: %.$(ASEXT) 139 | @mkdir -p $(dir $@) 140 | $(CC) $(ASFLAGS) $(GEN_DEP_FLAG) -c -o $@ $< 141 | @if [ $$(nm -g --format="posix" $@ | grep -c "^main T") -eq 1 ]; then \ 142 | echo "$(patsubst $(BUILD_ROOT)/%.o,%.$(ASEXT),$@)" >> $(em_f_entries); \ 143 | sort -u $(em_f_entries) -o $(em_f_entries); \ 144 | fi; 145 | 146 | # include all generated dependency files 147 | ifneq ($(strip $(em_all_objects)),) 148 | sinclude $(em_all_objects:.o=.d) 149 | endif 150 | 151 | # Read detected entries from file and filter out the non-existed source 152 | em_entry_list = $(ENTRY_LIST) $(filter $(CSRC) $(CXXSRC) $(ASSRC) ,$(shell cat $(em_f_entries) 2>/dev/null)) 153 | 154 | Em_entry = $(if $(filter none NONE,$(1)),,$(1)) 155 | Em_objects = $(call Em_src2obj,$(filter-out $(em_entry_list),$(CSRC) $(CXXSRC) $(ASSRC)) $(call Em_entry,$(1)),$(BUILD_ROOT)) 156 | Em_src2target = $(foreach src,$1,$(BUILD_ROOT)/$(notdir $(basename $(src)))) 157 | 158 | $(em_all_objects): $(filter-out $(BUILD_ROOT)/%,$(MAKEFILE_LIST)) 159 | 160 | $(BUILD_ROOT)/em_targets.mk: $(em_all_objects) 161 | @rm -f $@ 162 | @$(foreach f,$(em_entry_list), \ 163 | echo 'all: $(call Em_src2target,$f)' >> $@; \ 164 | echo '$(call Em_src2target,$f): $(call Em_objects,$f)' >> $@; \ 165 | echo ' $(em_linker) $$^ $(LDFLAGS) -o $$@ $(LOADLIBES) $(LDLIBS)' >> $@; \ 166 | ) 167 | 168 | # This recipe to handle rule "all: foo.so" or command "make foo.so" 169 | $(BUILD_ROOT)/lib%.so lib%.so: $(call Em_objects,NONE) 170 | @echo 171 | $(em_linker) $(call Em_objects,NONE) $(LDFLAGS) -o $@ $(LOADLIBES) $(LDLIBS) 172 | 173 | # This recipe to handle rule "all: foo.a" or command "make foo.a" 174 | $(BUILD_ROOT)/lib%.a lib%.a: $(call Em_objects,NONE) 175 | $(AR) $(ARFLAGS) $@ $(call Em_objects,NONE) 176 | 177 | all $(sort $(call Em_src2target,$(CSRC) $(CXXSRC) $(ASSRC))): $(em_all_objects) $(BUILD_ROOT)/em_targets.mk 178 | @$(if $(strip $(em_entry_list)), \ 179 | $(MAKE) --no-print-directory -f $(BUILD_ROOT)/em_targets.mk \ 180 | $(filter-out %.so %.a %.o %.d,$(filter $(BUILD_ROOT)/%,$(MAKECMDGOALS))) \ 181 | ) 182 | 183 | 184 | # "check" is the standard target from standard makefile conventions 185 | check: test 186 | test: all 187 | @echo "--- running tests ..." 188 | @set -e; $(foreach f,$(em_entry_list), $(if $(call IS_TEST,$f), \ 189 | $(call Em_src2target,$f); \ 190 | ,)) 191 | @echo "--- test complete." 192 | 193 | clean: em_clean 194 | .PHONY: em_clean test check 195 | em_clean: 196 | if [ -d $(BUILD_ROOT) ]; then find $(BUILD_ROOT) '(' -name "*.o" -o -name "*.d" -o -name "*.a" -o -name "*.so" -o -name "em_*" ')' -exec rm -f '{}' ';' ; fi 197 | 198 | -------------------------------------------------------------------------------- /samples/basics/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | -------------------------------------------------------------------------------- /samples/basics/add.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "math/add.h" 4 | 5 | int main(){ 6 | std::cout<<"add(5,6) returns "< 15 | 16 | #include "math/add.h" 17 | #include 18 | 19 | int main(){ 20 | assert(add(1,1)==3); 21 | return 0; 22 | } 23 | ' > add_test.cpp 24 | 25 | echo 26 | echo make test 27 | make test 28 | 29 | -------------------------------------------------------------------------------- /samples/so/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS=-I. -fPIC 2 | 3 | all: bin/libmylib.so 4 | %.so:LDFLAGS=-shared 5 | 6 | include ../../easymake.mk 7 | -------------------------------------------------------------------------------- /samples/so/add_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "math/add.h" 3 | 4 | using namespace std; 5 | 6 | int main(){ 7 | cout<<"add(8,8)="< 4 | 5 | using namespace std; 6 | 7 | int main(){ 8 | cout<<"add(8,8)="< 2 | 3 | #include "math/add.h" 4 | 5 | int hello_world(); 6 | 7 | int main(){ 8 | std::cout<<"please enter two integer:"<>a>>b; 12 | 13 | std::cout<<"add("< 2 | using std::cout; 3 | using std::endl; 4 | 5 | int hello_world(){ 6 | cout<<"hello world"<