/********************************************************************** * Premake - gnu_cpp.c * The GNU C/C++ makefile target * * Copyright (c) 2002-2005 Jason Perkins and the Premake project * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License in the file LICENSE.txt for details. **********************************************************************/ #include #include #include "premake.h" #include "gnu.h" #include "os.h" static const char* filterLinks(const char* name); static const char* listCppSources(const char* name); static const char* listRcSources(const char* name); static const char* listCppTargets(const char* name); static const char* listRcTargets(const char* name); static const char* listLinkerDeps(const char* name); int gnu_cpp() { int i; const char* prefix = (g_verbose) ? "" : "@"; /* Open package makefile and write the header */ if (gnu_pkgOwnsPath()) strcpy(g_buffer, path_join(prj_get_pkgpath(), "Makefile", "")); else strcpy(g_buffer, path_join(prj_get_pkgpath(), prj_get_pkgname(), DOT_MAKE)); io_openfile(g_buffer); io_print("# %s ", prj_is_lang("c++") ? "C++" : "C"); if (prj_is_kind("exe")) io_print("Console Executable"); else if (prj_is_kind("winexe")) io_print("Windowed Executable"); else if (prj_is_kind("dll")) io_print("Shared Library"); else if (prj_is_kind("lib")) io_print("Static Library"); io_print(" Makefile autogenerated by premake\n"); io_print("# Don't edit this file! Instead edit `premake.lua` then rerun `make`\n\n"); /* Set a default configuration */ prj_select_config(0); io_print("ifndef CONFIG\n"); io_print(" CONFIG=%s\n", prj_get_cfgname()); io_print("endif\n\n"); /* Process the build configurations */ for (i = 0; i < prj_get_numconfigs(); ++i) { prj_select_config(i); io_print("ifeq ($(CONFIG),%s)\n", prj_get_cfgname()); io_print(" BINDIR := %s\n", prj_get_bindir()); io_print(" LIBDIR := %s\n", prj_get_libdir()); io_print(" OBJDIR := %s\n", prj_get_objdir()); io_print(" OUTDIR := %s\n", prj_get_outdir()); /* Write preprocessor flags - how to generate dependencies for DMC? */ io_print(" CPPFLAGS :="); if (!matches(g_cc, "dmc")) io_print(" -MMD"); print_list(prj_get_defines(), " -D \"", "\"", "", NULL); print_list(prj_get_incpaths(), " -I \"", "\"", "", NULL); io_print("\n"); /* Write C flags */ io_print(" CFLAGS += $(CPPFLAGS) $(TARGET_ARCH)"); if (prj_is_kind("dll") && !os_is("windows")) io_print(" -fPIC"); if (!prj_has_flag("no-symbols")) io_print(" -g"); if (prj_has_flag("optimize-size")) io_print(" -Os"); if (prj_has_flag("optimize-speed")) io_print(" -O3"); if (prj_has_flag("optimize") && !prj_has_flag("optimize-size") && !prj_has_flag("optimize-speed")) io_print(" -O2"); if (prj_has_flag("extra-warnings")) io_print(" -Wall"); if (prj_has_flag("fatal-warnings")) io_print(" -Werror"); if (prj_has_flag("no-frame-pointer")) io_print(" -fomit-frame-pointer"); print_list(prj_get_buildoptions(), " ", "", "", NULL); io_print("\n"); /* Write C++ flags */ io_print(" CXXFLAGS := $(CFLAGS)"); if (prj_has_flag("no-exceptions")) io_print(" --no-exceptions"); if (prj_has_flag("no-rtti")) io_print(" --no-rtti"); io_print("\n"); /* Write linker flags */ io_print(" LDFLAGS += -L$(BINDIR) -L$(LIBDIR)"); if (prj_is_kind("dll") && (g_cc == NULL || matches(g_cc, "gcc"))) { if (!os_is("macosx")) io_print(" -shared"); /* Create import libraries for DLLs if we're using Cygwin or MinGW. * This seems fragile to me, but I'm going to let it sit and see * if anyone complains about it. */ if (!prj_has_flag("no-import-lib") && os_is("windows")) { const char* str; io_print(" -Wl,--out-implib=\"%s", prj_get_libdir()); if (prj_get_prefix() == NULL) str = "lib"; else str = prj_get_prefix(); if (prj_has_importlibname()) io_print("/%s%s.a\"", str, prj_get_importlibname()); else io_print("/%s%s.a\"", str, path_getbasename(prj_get_target())); } } if (prj_is_kind("winexe")) io_print(" -mwindows"); /* OS X has a bug, see http://lists.apple.com/archives/Darwin-dev/2006/Sep/msg00084.html */ if (prj_has_flag("no-symbols")) { if (os_is("macosx")) io_print(" -Wl,-x"); else io_print(" -s"); } if (os_is("macosx") && prj_has_flag("dylib")) io_print(" -dynamiclib -flat_namespace"); print_list(prj_get_linkoptions(), " ", "", "", NULL); print_list(prj_get_libpaths(), " -L\"", "\"", "", NULL); print_list(prj_get_links(), " ", "", "", filterLinks); io_print("\n"); /* Build a list of libraries this target depends on */ io_print(" LDDEPS :="); print_list(prj_get_links(), " ", "", "", listLinkerDeps); io_print("\n"); /* Build a list of flags for the resource compiler */ io_print(" RESFLAGS :="); print_list(prj_get_resdefines(), " -D \"", "\"", "", NULL); print_list(prj_get_respaths(), " -I \"", "\"", "", NULL); print_list(prj_get_resoptions(), " ", "", "", NULL); io_print("\n"); /* Build the target name */ io_print(" TARGET := %s\n", path_getname(prj_get_target())); if (os_is("macosx") && prj_is_kind("winexe")) io_print(" MACAPP := %s.app/Contents\n", path_getname(prj_get_target())); /* Build command */ io_print(" BLDCMD = "); if (prj_is_kind("lib")) io_print("ar -rcs $(OUTDIR)/$(TARGET) $(OBJECTS) $(TARGET_ARCH)"); else io_print("$(%s) -o $(OUTDIR)/$(TARGET) $(OBJECTS) $(LDFLAGS) $(RESOURCES) $(TARGET_ARCH)", prj_is_lang("c") ? "CC" : "CXX"); io_print("\n"); /* Pre-build commands */ if (prj_get_numprebuildcommands() > 0) { io_print("\n define prebuildcmd\n"); io_print("\t@echo Running pre-build commands...\n"); print_list(prj_get_prebuildcommands(), "\t", "\n", "", NULL); io_print(" endef\n"); } /* Pre-link commands */ if (prj_get_numprelinkcommands() > 0) { io_print("\n define prelinkcmd\n"); io_print("\t@echo Running pre-link commands...\n"); print_list(prj_get_prelinkcommands(), "\t", "\n", "", NULL); io_print(" endef\n"); } /* Post-build commands */ if (prj_get_numpostbuildcommands() > 0) { io_print("\n define postbuildcmd\n"); io_print("\t@echo Running post-build commands...\n"); print_list(prj_get_postbuildcommands(), "\t", "\n", "", NULL); io_print(" endef\n"); } io_print("endif\n\n"); } /* Write out the list of object file targets for all C/C++ sources */ io_print("OBJECTS := \\\n"); print_list(prj_get_files(), "\t$(OBJDIR)/", " \\\n", "", listCppSources); io_print("\n"); /* Write out the list of resource files for windows targets */ if (os_is("windows")) { io_print("RESOURCES := \\\n"); print_list(prj_get_files(), "\t$(OBJDIR)/", " \\\n", "", listRcSources); io_print("\n"); } /* Try to figure out how to safely create a directory */ io_print("MKDIR_TYPE := msdos\n"); io_print("CMD := $(subst \\,\\\\,$(ComSpec)$(COMSPEC))\n"); io_print("ifeq (,$(CMD))\n"); io_print(" MKDIR_TYPE := posix\n"); io_print("endif\n"); io_print("ifeq (/bin/sh.exe,$(SHELL))\n"); io_print(" MKDIR_TYPE := posix\n"); io_print("endif\n"); io_print("ifeq ($(MKDIR_TYPE),posix)\n"); io_print(" CMD_MKBINDIR := mkdir -p $(BINDIR)\n"); io_print(" CMD_MKLIBDIR := mkdir -p $(LIBDIR)\n"); io_print(" CMD_MKOUTDIR := mkdir -p $(OUTDIR)\n"); io_print(" CMD_MKOBJDIR := mkdir -p $(OBJDIR)\n"); io_print("else\n"); io_print(" CMD_MKBINDIR := $(CMD) /c if not exist $(subst /,\\\\,$(BINDIR)) mkdir $(subst /,\\\\,$(BINDIR))\n"); io_print(" CMD_MKLIBDIR := $(CMD) /c if not exist $(subst /,\\\\,$(LIBDIR)) mkdir $(subst /,\\\\,$(LIBDIR))\n"); io_print(" CMD_MKOUTDIR := $(CMD) /c if not exist $(subst /,\\\\,$(OUTDIR)) mkdir $(subst /,\\\\,$(OUTDIR))\n"); io_print(" CMD_MKOBJDIR := $(CMD) /c if not exist $(subst /,\\\\,$(OBJDIR)) mkdir $(subst /,\\\\,$(OBJDIR))\n"); io_print("endif\n"); io_print("\n"); io_print(".PHONY: clean"); if (prj_get_numprebuildcommands() > 0) io_print(" prebuild"); if (prj_get_numprelinkcommands() > 0) io_print(" prelink"); io_print("\n\n"); /* Write the main build target */ if (os_is("macosx") && prj_is_kind("winexe")) { io_print("all: $(OUTDIR)/$(MACAPP)/PkgInfo $(OUTDIR)/$(MACAPP)/Info.plist $(OUTDIR)/$(MACAPP)/MacOS/$(TARGET)\n\n"); io_print("$(OUTDIR)/$(MACAPP)/MacOS/$(TARGET)"); } else { io_print("$(OUTDIR)/$(TARGET)"); } io_print(": "); if (prj_get_numprebuildcommands() > 0) io_print("prebuild "); io_print("$(OBJECTS) $(LDDEPS) $(RESOURCES)"); if (prj_get_numprelinkcommands() > 0) io_print(" prelink"); io_print("\n"); if (!g_verbose) io_print("\t@echo Linking %s\n", prj_get_pkgname()); io_print("\t-%s$(CMD_MKBINDIR)\n", prefix); io_print("\t-%s$(CMD_MKLIBDIR)\n", prefix); io_print("\t-%s$(CMD_MKOUTDIR)\n", prefix); if (os_is("macosx") && prj_is_kind("winexe")) io_print("\t-%sif [ ! -d $(OUTDIR)/$(MACAPP)/MacOS ]; then mkdir -p $(OUTDIR)/$(MACAPP)/MacOS; fi\n", prefix); io_print("\t%s$(BLDCMD)\n", prefix); if (prj_get_numpostbuildcommands() > 0) io_print("\t$(postbuildcmd)\n"); io_print("\n"); if (prj_get_numprebuildcommands() > 0) { io_print("prebuild:\n"); io_print("\t$(prebuildcmd)\n\n"); } if (prj_get_numprelinkcommands() > 0) { io_print("prelink:\n"); io_print("\t$(prelinkcmd)\n\n"); } if (os_is("macosx") && prj_is_kind("winexe")) { io_print("$(OUTDIR)/$(MACAPP)/PkgInfo:\n\n"); io_print("$(OUTDIR)/$(MACAPP)/Info.plist:\n\n"); } /* Write the "clean" target */ io_print("clean:\n"); io_print("\t@echo Cleaning %s\n", prj_get_pkgname()); io_print("ifeq ($(MKDIR_TYPE),posix)\n"); if (os_is("macosx") && prj_is_kind("winexe")) io_print("\t-%srm -rf $(OUTDIR)/$(TARGET).app\n", prefix); else io_print("\t-%srm -f $(OUTDIR)/$(TARGET)\n", prefix); io_print("\t-%srm -rf $(OBJDIR)\n", prefix); io_print("else\n"); io_print("\t-%sif exist $(subst /,\\,$(OUTDIR)/$(TARGET)) del /q $(subst /,\\,$(OUTDIR)/$(TARGET))\n", prefix); io_print("\t-%sif exist $(subst /,\\,$(OBJDIR)) del /q $(subst /,\\,$(OBJDIR))\n", prefix); io_print("\t-%sif exist $(subst /,\\,$(OBJDIR)) rmdir /s /q $(subst /,\\,$(OBJDIR))\n", prefix); io_print("endif\n\n"); /* Write static patterns for each source file. Note that in earlier * versions I used pattern rules instead of listing each file. It worked * fine but made it more difficult to test and also required the use of * VPATH which I didn't like. This new approach of listing each file * helps testing and opens the way for per-file configurations */ print_list(prj_get_files(), "", "\n", "", listCppTargets); if (os_is("windows")) print_list(prj_get_files(), "", "", "", listRcTargets); /* Include the automatically generated dependency lists */ io_print("-include $(OBJECTS:%%.o=%%.d)\n\n"); io_closefile(); return 1; } /************************************************************************ * Checks each entry in the list of package links. If the entry refers * to a sibling package, returns the path to that package's output ***********************************************************************/ static const char* filterLinks(const char* name) { int i = prj_find_package(name); if (i >= 0) { /* is a sibling */ const char* lang = prj_get_language_for(i); if (matches(lang, "c++") || matches(lang, "c")) { const char* dir; const char* target; target = prj_get_target_for(i); strcpy(g_buffer, ""); /* Add -L if path to sibling libraries, so they don't get * encoded in this linking binary (see bug #1729227) */ dir = path_getdir(target); if (!prj_has_libpath(dir)) { strcat(g_buffer, "-L"); strcat(g_buffer, dir); strcat(g_buffer, " "); } strcat(g_buffer, "-l"); strcat(g_buffer, prj_get_targetname_for(i)); return g_buffer; } else { return NULL; } } else { /* is not a sibling, just output directly */ strcpy(g_buffer, "-l"); strcat(g_buffer, name); return g_buffer; } } /************************************************************************ * Checks each source code file and filters out everything that is * not a C or C++ file ***********************************************************************/ static const char* listCppSources(const char* name) { if (is_cpp(name)) { strcpy(g_buffer, path_getbasename(name)); strcat(g_buffer, ".o"); return g_buffer; } return NULL; } /************************************************************************ * Checks each source code file and filters out everything that is * not a windows resource file ***********************************************************************/ static const char* listRcSources(const char* name) { const char* ext = path_getextension(name); if (matches(ext, ".rc")) { strcpy(g_buffer, path_getbasename(name)); strcat(g_buffer, ".res"); return g_buffer; } return NULL; } /************************************************************************ * Creates the makefile build rules for all source code files ***********************************************************************/ static const char* listCppTargets(const char* name) { if (is_cpp(name)) { const char* ext = path_getextension(name); sprintf(g_buffer, "$(OBJDIR)/%s.o: %s\n", path_getbasename(name), name); strcat(g_buffer, "\t-"); if (!g_verbose) strcat(g_buffer, "@"); strcat(g_buffer, "$(CMD_MKOBJDIR)\n"); if (!g_verbose) strcat(g_buffer, "\t@echo $(notdir $<)\n"); strcat(g_buffer, "\t"); if (!g_verbose) strcat(g_buffer, "@"); if (matches(g_cc, "dmc")) { /* Digital Mars compiler build step */ /* FIXME: How to handle assembly files with DMC? */ if (matches(ext, ".c")) strcat(g_buffer, "dmc $(CFLAGS) -o $@ -c $<\n"); else if (!matches(ext, ".s")) strcat(g_buffer, "dmc -cpp -Ae -Ar -mn $(CXXFLAGS) -o $@ -c $<\n"); } else { /* GNU GCC compiler build step */ if (matches(ext, ".s")) strcat(g_buffer, "$(CC) -x assembler-with-cpp $(CPPFLAGS) -o $@ -c $<\n"); else if (matches(ext, ".c")) strcat(g_buffer, "$(CC) $(CFLAGS) -o $@ -c $<\n"); else strcat(g_buffer, "$(CXX) $(CXXFLAGS) -o $@ -c $<\n"); } return g_buffer; } else { return NULL; } } /************************************************************************ * Creates the makefile build rules for windows resource files ***********************************************************************/ static const char* listRcTargets(const char* name) { const char* prefix = (g_verbose) ? "" : "@"; if (matches(path_getextension(name), ".rc")) { const char* base = path_getbasename(name); io_print("$(OBJDIR)/%s.res: %s\n", base, name); io_print("\t-%s$(CMD_MKOBJDIR)\n", prefix); if (!g_verbose) io_print("\t@echo $(notdir $<)\n"); if (matches(g_cc, "dmc")) io_print("\t%srcc $< -o$@ $(RESFLAGS)\n", prefix); else io_print("\t%swindres $< -O coff -o $@ $(RESFLAGS)\n", prefix); io_print("\n"); } return NULL; } /************************************************************************ * This is called by the code that builds the list of dependencies for * the link step. It looks for sibling projects, and then returns the * full path to that target's output. So if an executable package * depends on a library package, the library filename will be listed * as a dependency ***********************************************************************/ static const char* listLinkerDeps(const char* name) { int i = prj_find_package(name); if (i >= 0) { return prj_get_target_for(i); } return NULL; }