简体   繁体   English

不确定如何创建MakeFIle

[英]Unsure how to create a MakeFIle

I am having problems with creating a makefile for a project containing 3 c++ files. 我在为包含3个c ++文件的项目创建makefile时遇到问题。

While in the directory containing the files (tree.h, helperMethods.h, and main.cpp), I first use the command emacs makefile.make . 在包含文件(tree.h,helperMethods.h和main.cpp)的目录中时,我首先使用命令emacs makefile.make Then, in emacs, I write the makefile as: 然后,在emacs中,我将makefile编写为:

all: tree.h helperMethods.h main.cpp
[tab]g++ -std=c++0x -o project3 tree.h helperMethods.h main.cpp

I have verified manually that the command g++ -std=c++0x -o project3 tree.h helperMethods.h main.cpp does indeed compile the three files and make an executable that works at it should. 我已经手动验证了命令g ++ -std = c ++ 0x -o project3 tree.h helperMethods.h main.cpp确实确实编译了这三个文件并制作了一个可以运行的可执行文件。

After this, I save the makefile, exit out of emacs, and attempt to run it. 之后,我保存了makefile,退出了emacs,然后尝试运行它。

First I used make but this returned: 首先,我使用了make,但是返回了:

make: *** No targets specified and no makefile found.  Stop.

Then I tried using make -f makefile.make but this didn't work either and returned: 然后我尝试使用make -f makefile.make,但这也不起作用并返回:

make: Nothing to be done for `all'.

At this point, I'm unsure as to why the makefile wasn't constructed properly. 在这一点上,我不确定为什么makefile的构造不正确。 I don't have very much experience with makefiles; 我对makefile没有太多经验; I'm pretty sure that I have the commands in the body of the makefile correct but I didn't set it up properly when I wrote it. 我很确定我的makefile主体中的命令正确无误,但在编写时没有正确设置。

If it is at all relevant, here are the 3 files: 如果完全相关,那么这里是3个文件:

tree.h: tree.h中:

#ifndef tree_h
#define tree_h
#include <vector>

using namespace std;

template<class T>
class tree
{
public:
    tree<T> * parent;
    T element;
    vector<tree<T> > nodes;
    tree(const T& theElement)
    {
        element = theElement;
        nodes = vector<tree<T> >();
    }
    tree()
    {
        nodes = vector<tree<T> >();
    }
};

#endif

helperMethods.h helperMethods.h

#ifndef helperMethods_h
#define helperMethods_h

#include "tree.h"

#include <tuple>
#include <string>
#include <iostream>
#include <vector>

using namespace std;

string printName(tuple<string, string, string> myTuple){
    string ret = "";
    if(std::get<0>(myTuple).length() > 0)
        ret += get<0>(myTuple) + " ";
    if(std::get<1>(myTuple).length() > 0 || std::get<2>(myTuple).length() > 0)
        ret += "(";
    if(std::get<1>(myTuple).length() > 0)
        ret += get<1>(myTuple);
    if(std::get<1>(myTuple).length() > 0 && std::get<2>(myTuple).length() > 0)
        ret += ", ";
    if(std::get<2>(myTuple).length() > 0)
        ret += get<2>(myTuple);
    if(std::get<1>(myTuple).length() > 0 || std::get<2>(myTuple).length() > 0)
        ret += ")";
    return ret;
}

bool tupleContain(tuple<string, string, string> myTuple, string myString){
    return (std::get<0>(myTuple).compare(myString) == 0) || (std::get<1>(myTuple).compare(myString) == 0);
}

void findElement(tree<tuple<string, string, string> > myTree, string myString, bool* found, vector<int> *ret)
{
    if(tupleContain(myTree.element, myString))
        *found = true;
    if(! * found)
    {
        for(int counter = 0; counter < (int)myTree.nodes.size(); counter ++)
        {
            if(!* found)
            {
                (*ret).push_back(counter);
                findElement(myTree.nodes.at(counter), myString, found, ret);
                if(!* found)
                    (*ret).pop_back();
            }
        }
    }
}

void getLineage(tree<tuple<string, string, string> > myTree, string myString){
    bool dummyForFound = false;
    bool * found = & dummyForFound;
    vector<int> lineage = vector<int>();
    vector<int> * pointer = & lineage;
    findElement(myTree, myString, found, &lineage);
    if(lineage.size() == 0)
    {
        cout << "Species not present" << endl;
        return;
    }
    vector<string> printString = vector<string>(lineage.size() + 1);
    tree<tuple<string, string, string> > * currentNodePointer = & myTree;
    for(int counter = 0; counter <= (int) lineage.size(); counter ++)
    {
        string currentLine = "";
        for(int counter2 = 0; counter2 < 2*((int) lineage.size() - counter); counter2 ++)
            currentLine += ">";
        if(counter != lineage.size())
            currentLine += " ";
        tree<tuple<string, string, string> > currentNode = * currentNodePointer;
        currentLine += printName(currentNode.element);
        if(counter < (int) lineage.size())
        {
            int foo = lineage.at(counter);
            tree<tuple<string, string, string> > currentNodeDummy = currentNode.nodes.at(foo);
            *currentNodePointer = currentNodeDummy;
        }
        printString.at(counter) = currentLine;
    }
    for(int counter = 0; counter < (int) printString.size(); counter ++)
        cout << printString.at(printString.size() - counter - 1) << endl;
    cout << endl;
}

void getCommonLineage(tree<tuple<string, string, string> > myTree , string name1, string name2)
{
    bool dummyForFound = false;
    bool * found = & dummyForFound;
    vector<int> lineage1 = vector<int>();
    vector<int> * pointer1 = & lineage1;
    vector<int> lineage2 = vector<int>();
    vector<int> * pointer2 = & lineage2;
    findElement(myTree, name1, found, pointer1);
    * found = false;
    findElement(myTree, name2, found, pointer2);
    if(lineage2.size() == 0 || lineage1.size() == 0)
    {
        cout << "At least one species not present." << endl;
        return;
    }
    bool stillSame = lineage1.at(0) == lineage2.at(0);
    cout << "Level[0] Common Ancestor: ROOT (ROOT, ROOT)" << endl;
    tree<tuple<string, string, string>> * lastSharedNode = & myTree;
    int finalCounter = 0;
    for(int counter = 0; counter < (int) min(lineage1.size(), lineage2.size()) && stillSame; counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * lastSharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage1.at(counter));
        *lastSharedNode = currentNode;
        if(counter < (int) min(lineage1.size(), lineage2.size()) - 1 && lineage1.at(counter + 1) != lineage2.at(counter + 1))
            stillSame = false;
        tuple<string, string, string> currentElement = currentNode.element;
        cout << "Level[" << counter + 1 << "] Commont Ancestor: " << printName(currentElement) << endl;
        finalCounter ++;
    }
    cout << endl;
    cout << "Ancestry unique to " << name1 << endl;
    tree<tuple<string, string, string> > savedNode = *lastSharedNode;
    tree<tuple<string, string, string> > * currentUnsharedNode = lastSharedNode;
    for(int counter = finalCounter; counter < (int) lineage1.size(); counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * currentUnsharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage1.at(counter));
        tuple<string, string, string> currentElement = currentNode.element;
        *currentUnsharedNode = currentNode;
        cout << "Level[" << counter + 1 << "] ";
        if(counter == lineage1.size() - 1)
            cout << "Species of interest: ";
        cout << printName(currentElement) << endl;
    }
    cout << endl;
    currentUnsharedNode = &savedNode;
    cout << "Ancestry unique to " << name2 << endl;
    for(int counter = finalCounter; counter < (int) lineage2.size(); counter ++)
    {
        tree<tuple<string, string, string> > dummyNode = * currentUnsharedNode;
        tree<tuple<string, string, string> > currentNode = dummyNode.nodes.at(lineage2.at(counter));
        tuple<string, string, string> currentElement = currentNode.element;
        *currentUnsharedNode = currentNode;
        cout << "Level[" << counter + 1 << "] ";
        if(counter == lineage2.size() - 1)
            cout << "Species of interest: ";
        cout << printName(currentElement) << endl;
    }
    cout << endl;
}
#endif

main.cpp main.cpp中

#include "rapidxml.h"
#include "tree.h"
#include "helperMethods.h"

#include <string>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <queue>
#include <tuple>

using namespace rapidxml;

int main(int argc, const char * arv[]){
    tuple<string, string, string> human ("Human", "Homo sapiens", "Species");
    tuple<string, string, string> apes ("Apes", "", "");
    tuple<string, string, string> dogs ("Dogs", "", "");
    tuple<string, string, string> root ("Root", "", "");
    tuple<string, string, string> bears ("Bears", "", "");
    tuple<string, string, string> cat ("Cat", "", "");
    tuple<string, string, string> horse ("Horse", "", "");
    tree<tuple<string, string, string> > myTree = tree<tuple<string, string, string>>(root);
    tree<tuple<string, string, string> > b = tree<tuple<string, string, string>>(dogs);
    tree<tuple<string, string, string> > c = tree<tuple<string, string, string>>(apes);
    tree<tuple<string, string, string> > d = tree<tuple<string, string, string>>(bears);
    tree<tuple<string, string, string> > e = tree<tuple<string, string, string>>(horse);
    tree<tuple<string, string, string> > f = tree<tuple<string, string, string>>(human);
    tree<tuple<string, string, string> > h = tree<tuple<string, string, string>>(cat);
    d.nodes.push_back(f);
    e.nodes.push_back(h);
    b.nodes.push_back(d);
    b.nodes.push_back(e);
    myTree.nodes.push_back(b);
    myTree.nodes.push_back(c);
    cout << printName(myTree.nodes.at(0).element);

    cout << "Welcome to my Tree of Life program!" << endl << endl;

    int choice = 1;
    while(choice == 1 || choice == 2) {
        cout << "Please choose from the following options:" << endl;
        cout << "   1.Get the lineage of a species" << endl;
        cout << "   2.Get the commmon lineage of two species" << endl;
        cout << "   3.Exit program" << endl << endl;
        cin >> choice;
        cout << endl;
        if(choice == 1)
        {
            cout << "Please enter the name of the species of interest:" << endl;
            string name;
            cin >> name;
            cout << endl;
            getLineage(myTree, name);
        }
        if(choice == 2)
        {
            string name1, name2;
            cout << "Please enter the name of the first species: " << endl;
            cin >> name1;
            cout << "Please enter the name of the second species: " << endl;
            cin >> name2;
            getCommonLineage(myTree, name1, name2);
        }
    }
    return 0;
}

Makefiles are usually called Makefile (although makefile works, too) without any extension. Makefile通常称为Makefile (尽管makefile也可以),没有任何扩展名。 If you use an extension ( not recommended ), you have to tell make the name of the makefile. 如果使用扩展名( 不推荐使用 ),则必须告诉make makefile的名称。 That's tedious and unnecessary. 那是乏味且不必要的。

Second, you don't need to put header files in the compile command line, and you shouldn't do so. 其次,您不需要将头文件放在编译命令行中,也不必这样做。 You use #include lines in your file to specify the header files. 您可以在文件中使用#include行来指定头文件。 So your compile command might look like this: 因此,您的编译命令可能如下所示:

g++ -std=c++0x -o project3 main.cpp

Now, in that command: 现在,在该命令中:

  • main.cpp is the source main.cpp

  • project3 is the target (ie, the file which will be created.) project3目标 (即将创建的文件。)

Also: 也:

  • tree.h and helperMethods.h are needed for the compile to work; tree.hhelperMethods.h是编译工作所必需的; the compilation depends on this files. 编译取决于此文件。 (You know that, but they don't show up in the command-line so its not obvious.) Furthermore, if they change, you have to recompile your file. (您知道这一点,但是它们没有出现在命令行中,因此并不明显。)此外,如果更改了它们,则必须重新编译文件。

A makefile explains how to make a target from its sources , and also lists the dependencies of the target . 一个makefile文件解释了如何从其来源制作目标 ,并列出了目标依赖关系 (Technically, make doesn't distinguish between sources and dependencies ; it considers both of them prerequisites . But it's convenient to recognize the difference.) (从技术上讲,make不会区分依赖项 ;它考虑了两者的先决条件 。但是,可以方便地识别出差异。)

So for the above command, the entire make recipe might look like this: 因此,对于以上命令,整个make配方可能如下所示:

project3: main.cpp tree.h helperMethods.h 
        g++ -std=c++0x -o project3 main.cpp

Normally, we don't use filenames like main.cpp ; 通常,我们不使用像main.cpp这样的文件名; rather, we'd call the main file for project3 project3.cpp , just as the target (output) file is project3 . 相反,我们将调用project3的主文件project3.cpp ,就像目标(输出)文件是project3 In fact, if you did that, and you didn't have any other dependencies (header files), you could type: 实际上,如果您这样做了,并且没有任何其他依赖项(头文件),则可以输入:

make project3

without a makefile at all , and make would come up with the command: 完全没有makefilemake会提出以下命令:

g++ -o project3 project3.cpp

In this case, that command would be wrong because it doesn't specify the correct C++ standard. 在这种情况下,该命令将是错误的,因为它没有指定正确的C ++标准。 But it often works, and it's a good reason to name your targets and sources with the same basename. 但这经常有效,这是用相同的基本名称命名目标和源的一个很好的理由。

One more thing: your file tree.h is a header-only library, which is fine. 还有一件事:您的文件tree.h是仅标头的库,这很好。 But helperMethods.h is only pretending to be a header file. 但是helperMethods.h仅假装为头文件。 It's really a complete implementation. 这确实是一个完整的实现。 You should fix that. 你应该解决这个问题。

Also, it's really a bad idea to put using namespace std in a header file. 此外,在头文件中using namespace std 确实是一个坏主意。 Header files should explicitly use the std:: namespace prefix on everything which needs it. 头文件应该在需要它的所有内容上显式使用std::名称空间前缀。 It's generally not recommended to use using namespace std anywhere, but it's particularly bad in header files because it silently pollutes the default namespace of any file which includes the header. 通常不建议在任何地方using namespace std ,但这在头文件中尤其糟糕,因为它会默默地污染任何包含头文件的默认命名空间。 This can lead to very obscure bugs, or compilation errors. 这可能导致非常模糊的错误或编译错误。

School project, eh? 学校项目,对吗? project3 seems an awful arbitrary name for something you're working on yourself. project3似乎是您正在为自己工作的东西的一个可怕的任意名称。

You don't need to include .h files in your makefile processing because, strictly speaking, they never need to be compiled independently. 您无需在makefile处理中包括.h文件,因为严格来说,它们永远不需要独立编译。 Your makefile would need a rule for building main.o out of main.cpp, and then a rule for building an executable out of main.o. 您的makefile将需要一个从main.cpp中构建main.o的规则,然后一个从main.o中构建可执行文件的规则。 The header files should be added as dependencies of main.o, so that if either of them change, make will know to rebuild from there. 头文件应作为main.o的依赖项添加,这样,如果它们中的任何一个发生更改, make都将知道从那里进行重建。

Basically, it will need to look something like this: 基本上,它需要看起来像这样:

# http://stackoverflow.com/questions/15458126/unsure-how-to-create-a-makefile
all: project3

clean:
    -rm main.o project3

project3: main.o
    g++ -std=c++0x -o project3 main.o

main.o: main.cpp tree.h helperMethods.h
    g++ -std=c++0x -c main.cpp

Someone else may produce a dramatically different, but equally valid, makefile for this purpose. 为此,其他人可能会生成截然不同但同样有效的makefile。 There's a degree of freedom to the process. 这个过程有一定程度的自由。

What you have here is a rule to build the whole thing (keeping in mind that if no target is provided, the first one in the makefile is automatically chosen), rules to clean up any intermediate files, a rule to build the executable from the object files, and a rule to build the object file from the source files. 您所拥有的是构建整个对象的规则(请记住,如果未提供目标,则将自动选择makefile中的第一个目标),清除所有中间文件的规则,从中构建可执行文件的规则。目标文件,以及从源文件构建目标文件的规则。 This approach doesn't scale very well; 这种方法无法很好地扩展; it would be unpleasant to manage by hand all of the dependencies for larger, more complex makefiles, but in this very simple case it's fine. 手动管理较大,更复杂的makefile的所有依赖关系是不愉快的,但是在这种非常简单的情况下,这很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM