SRC, OBJ 및 BIN 하위 디렉토리가있는 C 프로젝트 용 Makefile을 어떻게 만들 수 있습니까?


95

몇 달 전에 저는 Makefile학교 과제를 위해 다음과 같은 일반적인 사항 을 생각해 냈습니다 .

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2010-11-05
#
# Changelog :
#   0.01 - first version
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc -std=c99 -c
# compiling flags here
CFLAGS   = -Wall -I.

LINKER   = gcc -o
# linking flags here
LFLAGS   = -Wall

SOURCES  := $(wildcard *.c)
INCLUDES := $(wildcard *.h)
OBJECTS  := $(SOURCES:.c=*.o)
rm       = rm -f

$(TARGET): obj
    @$(LINKER) $(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

obj: $(SOURCES) $(INCLUDES)
    @$(CC) $(CFLAGS) $(SOURCES)
    @echo "Compilation complete!"

clean:
    @$(rm) $(TARGET) $(OBJECTS)
    @echo "Cleanup complete!"

이것은 기본적으로 모든 컴파일 .c하고 .h생성하는 파일을 .o파일 및 실행 파일 projectname과 같은 폴더에있는 모든합니다.

자, 이것을 조금 밀고 싶습니다. 다음 디렉토리 구조로 C 프로젝트를 컴파일하기 위해 Makefile을 어떻게 작성할 수 있습니까?

 ./
 ./Makefile
 ./src/*.c;*.h
 ./obj/*.o
 ./bin/<executable>

즉, C 소스를 ./src/로 컴파일 ./obj/한 다음 모든 것을 링크하여 ./bin/.

다른 Makefile을 읽으려고했지만 위의 프로젝트 구조에서 작동하도록 만들 수는 없습니다. 대신 프로젝트가 모든 종류의 오류로 컴파일되지 않습니다. 물론 완전한 IDE (Monodevelop, Anjuta 등)를 사용할 수 있지만 솔직히 gEdit와 좋은 터미널을 선호합니다.

저에게 작업 솔루션을 제공 할 수있는 전문가가 있습니까? 또는 이것이 어떻게 수행 될 수 있는지에 대한 명확한 정보를 제공합니까? 감사합니다!

** 업데이트 (v4) **

최종 솔루션 :

# ------------------------------------------------
# Generic Makefile
#
# Author: yanick.rochon@gmail.com
# Date  : 2011-08-10
#
# Changelog :
#   2010-11-05 - first version
#   2011-08-10 - added structure : sources, objects, binaries
#                thanks to http://stackoverflow.com/users/128940/beta
#   2017-04-24 - changed order of linker params
# ------------------------------------------------

# project name (generate executable with this name)
TARGET   = projectname

CC       = gcc
# compiling flags here
CFLAGS   = -std=c99 -Wall -I.

LINKER   = gcc
# linking flags here
LFLAGS   = -Wall -I. -lm

# change these to proper directories where each file should be
SRCDIR   = src
OBJDIR   = obj
BINDIR   = bin

SOURCES  := $(wildcard $(SRCDIR)/*.c)
INCLUDES := $(wildcard $(SRCDIR)/*.h)
OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
rm       = rm -f


$(BINDIR)/$(TARGET): $(OBJECTS)
    @$(LINKER) $(OBJECTS) $(LFLAGS) -o $@
    @echo "Linking complete!"

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    @$(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

.PHONY: clean
clean:
    @$(rm) $(OBJECTS)
    @echo "Cleanup complete!"

.PHONY: remove
remove: clean
    @$(rm) $(BINDIR)/$(TARGET)
    @echo "Executable removed!"

여기서 구체적인 질문은 무엇입니까?
Oliver Charlesworth 2011 년

당신이 뭘 하려는지 잘 모르겠습니다.

업데이트 된 Makefile. 나는 가까운지고있어,하지만 어쨌든 것 같다, 그래서 나는 자동 변수에 문제가 있습니다
Yanick Rochon

방금 해결책을 찾았습니다. 누군가 더 나은 것을 찾고자한다면 Makefile은 여전히 ​​개선 될 수 있습니다.
Yanick Rochon

2
@YanickRochon 나는 당신의 영어 실력을 비판하려고 한 것이 아닙니다. 그러나 PHONY 타겟이 이해하기 위해서는 BANANA를 작성할 수 없습니다.) gnu.org/software/make/manual/html_node/Phony-Targets.html
joni

답변:


34

첫째, 다음 $(OBJECTS)과 같은 이유로 규칙에 문제가 있습니다.

  1. 그것은 일종의 무차별 적 이며 모든 객체의 모든 소스를 전제 조건으로 만듭니다.
  2. 종종 잘못된 소스를 사용 합니다 ( file1.o및에서 발견 한대로 file2.o)
  3. 개체에서 중지하는 대신 실행 파일을 빌드하려고합니다.
  4. 대상의 이름 ( foo.o)은 규칙이 실제로 생성 하는 이름 ( )이 아닙니다 obj/foo.o.

다음을 제안합니다.

OBJECTS  := $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)

$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@
    @echo "Compiled "$<" successfully!"

$(TARGET)규칙은 대상 이름이 실제로 규칙 빌드 무엇인지 설명하지 않는 것과 같은 문제가있다. 따라서 make여러 번 입력하면 Make는 이유가 없더라도 매번 대상을 다시 작성합니다. 작은 변경으로 다음 사항이 수정되었습니다.

$(BINDIR)/$(TARGET): $(OBJECTS)
    $(LINKER) $@ $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

모든 것이 순서대로 이루어지면 더 정교한 종속성 처리를 고려할 수 있습니다. 헤더 파일 중 하나를 수정하면이 메이크 파일은 어떤 개체 / 실행 파일을 다시 빌드해야하는지 알지 못합니다. 그러나 그것은 다른 날을 기다릴 수 있습니다.

편집 :
죄송합니다, $(OBJECTS)위 규칙의 일부를 생략했습니다 . 나는 그것을 고쳤다. (코드 샘플 내에서 "스트라이크"를 사용할 수 있기를 바랍니다.)


제안 된 변경 사항으로 다음과 같은 결과를 얻습니다.obj/file1.o: In function 'main': \n main.c:(.text+0x0): multiple definition of 'main' \n obj/main.o:main.c:(.text+0x0): first defined here
Yanick Rochon 2011-08-10

@Yanick Rochon : 여러 main기능이 있습니까? 아마 하나 file1.c와 하나 main.c? 그렇다면 이러한 개체를 연결할 수 없습니다. main실행 파일 에는 하나만있을 수 있습니다 .
베타 '

아니, 난하지 않습니다. 질문에 게시 한 마지막 버전에서는 모든 것이 잘 작동합니다. 내 Makefile을 당신이 제안한 것으로 변경하면 (그리고 당신이 말하는 것의 이점을 이해합니다) 그것이 내가 얻는 것입니다. 방금 붙여 넣었 file1.c지만 프로젝트의 모든 파일에 동일한 메시지를 제공합니다. 그리고 main.c는 IS 유일한 주요 기능을 하나 ... 그리고 main.c수입 file1.hfile2.h(사이에 관계가 없다 file1.cfile2.c),하지만 난 문제가 거기에서 오는 의심한다.
Yanick Rochon 2011 년

@Yanick Rochon : 내 $(OBJECTS)규칙 의 첫 번째 줄을 붙여 넣는 실수를 저질렀습니다 . 나는 그것을 편집했다. 잘못된 라인으로 오류가 발생했지만받은 것은 아닙니다 ...
Beta

6

-I컴파일러 플래그 (CFLAGS)에 플래그를 추가하여 컴파일러가 소스 파일을 찾아야하는 위치를 표시하고 -o 플래그를 추가하여 바이너리가 남아 있어야하는 위치를 표시 할 수 있습니다.

CFLAGS   = -Wall -I./src
TARGETPATH = ./bin

$(TARGET): obj
    @$(LINKER) $(TARGETPATH)/$(TARGET) $(LFLAGS) $(OBJECTS)
    @echo "Linking complete!"

오브젝트 파일을 obj디렉토리 에 드롭하려면 -o컴파일 할 때 옵션을 사용하십시오 . 또한 $@$< 자동 변수를 살펴보십시오 .

예를 들어,이 간단한 Makefile을 고려하십시오.

CFLAGS= -g -Wall -O3                                                            
OBJDIR= ./obj

SRCS=$(wildcard *.c)
OBJS=$(SRCS:.c=.o )
all:$(OBJS)

%.o: %.c 
   $(CC) $(CFLAGS) -c $< -o $(OBJDIR)/$@

업데이트>

makefile을 보면 -o플래그를 사용하고 있음을 알 수 있습니다 . 좋은. 계속 사용하되 출력 파일을 작성해야하는 위치를 나타내는 대상 디렉토리 변수를 추가하십시오.


좀 더 구체적으로 말씀해 주시겠습니까? 당신은 추가 의미합니까 -l ...받는 CFLAGS이미 거기에 ... -o링커에 인수 ( LINKER)
Yanick Rochon

예, CFLAGS 및 예, -o를 계속 사용하고 TARGETPATH ​​변수를 추가하십시오.

감사합니다. 수정했지만 여전히 뭔가 누락 된 것 같습니다 (질문에 대한 업데이트 참조)
Yanick Rochon

다만 make, 메이크 앉아 어디에서
Yanick Rochon

실행중인 명령을 읽을 수 없습니까? 예 : gcc -c yadayada. 예쁜 당신이 기대하는 것을 포함하지 않는 변수가있다

-1

나는 요즘 메이크 파일 작성을 중단했다. 만약 당신의 의도가 계속해서 배우는 것이라면, 그렇지 않으면 Eclipse CDT와 함께 제공되는 좋은 메이크 파일 생성기가있다. 빌드 트리에서 유지 관리 / 다중 프로젝트 지원을 원한다면 다음을 살펴보십시오.

https://github.com/dmoulding/boilermake 이건 꽤 괜찮은 걸 찾았습니다 ..!


3
의견 기반. OP의 질문에 대답하지 않습니다. Eclipse 환경을 가정합니다.
Nathaniel Johnson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.