1//===- GroupCmd.cpp -------------------------------------------------------===//
2//
3//                     The MCLinker Project
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "mcld/Script/GroupCmd.h"
10
11#include "mcld/LD/GroupReader.h"
12#include "mcld/MC/InputBuilder.h"
13#include "mcld/MC/Attribute.h"
14#include "mcld/Script/InputToken.h"
15#include "mcld/Script/StringList.h"
16#include "mcld/Support/MsgHandling.h"
17#include "mcld/Support/Path.h"
18#include "mcld/Support/raw_ostream.h"
19#include "mcld/InputTree.h"
20#include "mcld/LinkerScript.h"
21
22#include <llvm/Support/Casting.h>
23#include <cassert>
24
25namespace mcld {
26
27//===----------------------------------------------------------------------===//
28// GroupCmd
29//===----------------------------------------------------------------------===//
30GroupCmd::GroupCmd(StringList& pStringList,
31                   InputTree& pInputTree,
32                   InputBuilder& pBuilder,
33                   GroupReader& pGroupReader,
34                   const LinkerConfig& pConfig)
35    : ScriptCommand(ScriptCommand::GROUP),
36      m_StringList(pStringList),
37      m_InputTree(pInputTree),
38      m_Builder(pBuilder),
39      m_GroupReader(pGroupReader),
40      m_Config(pConfig) {
41}
42
43GroupCmd::~GroupCmd() {
44}
45
46void GroupCmd::dump() const {
47  mcld::outs() << "GROUP ( ";
48  bool prev = false, cur = false;
49  for (StringList::const_iterator it = m_StringList.begin(),
50                                  ie = m_StringList.end();
51       it != ie;
52       ++it) {
53    assert((*it)->kind() == StrToken::Input);
54    InputToken* input = llvm::cast<InputToken>(*it);
55    cur = input->asNeeded();
56    if (!prev && cur)
57      mcld::outs() << "AS_NEEDED ( ";
58    else if (prev && !cur)
59      mcld::outs() << " )";
60
61    if (input->type() == InputToken::NameSpec)
62      mcld::outs() << "-l";
63    mcld::outs() << input->name() << " ";
64
65    prev = cur;
66  }
67
68  if (!m_StringList.empty() && prev)
69    mcld::outs() << " )";
70
71  mcld::outs() << " )\n";
72}
73
74void GroupCmd::activate(Module& pModule) {
75  LinkerScript& script = pModule.getScript();
76  // construct the Group tree
77  m_Builder.setCurrentTree(m_InputTree);
78  // --start-group
79  m_Builder.enterGroup();
80  InputTree::iterator group = m_Builder.getCurrentNode();
81
82  for (StringList::const_iterator it = m_StringList.begin(),
83                                  ie = m_StringList.end();
84       it != ie;
85       ++it) {
86    assert((*it)->kind() == StrToken::Input);
87    InputToken* token = llvm::cast<InputToken>(*it);
88    if (token->asNeeded())
89      m_Builder.getAttributes().setAsNeeded();
90    else
91      m_Builder.getAttributes().unsetAsNeeded();
92
93    switch (token->type()) {
94      case InputToken::File: {
95        sys::fs::Path path;
96
97        // 1. Looking for file in the sysroot prefix, if a sysroot prefix is
98        // configured and the filename starts with '/'
99        if (script.hasSysroot() &&
100            (token->name().size() > 0 && token->name()[0] == '/')) {
101          path = script.sysroot();
102          path.append(token->name());
103        } else {
104          // 2. Try to open the file in CWD
105          path.assign(token->name());
106          if (!sys::fs::exists(path)) {
107            // 3. Search through the library search path
108            sys::fs::Path* p =
109                script.directories().find(token->name(), Input::Script);
110            if (p != NULL)
111              path = *p;
112          }
113        }
114
115        if (!sys::fs::exists(path))
116          fatal(diag::err_cannot_open_input) << path.filename() << path;
117
118        m_Builder.createNode<InputTree::Positional>(
119            path.filename().native(), path, Input::Unknown);
120        break;
121      }
122      case InputToken::NameSpec: {
123        const sys::fs::Path* path = NULL;
124        // find out the real path of the namespec.
125        if (m_Builder.getConstraint().isSharedSystem()) {
126          // In the system with shared object support, we can find both archive
127          // and shared object.
128          if (m_Builder.getAttributes().isStatic()) {
129            // with --static, we must search an archive.
130            path = script.directories().find(token->name(), Input::Archive);
131          } else {
132            // otherwise, with --Bdynamic, we can find either an archive or a
133            // shared object.
134            path = script.directories().find(token->name(), Input::DynObj);
135          }
136        } else {
137          // In the system without shared object support, only look for an
138          // archive
139          path = script.directories().find(token->name(), Input::Archive);
140        }
141
142        if (path == NULL)
143          fatal(diag::err_cannot_find_namespec) << token->name();
144
145        m_Builder.createNode<InputTree::Positional>(
146            token->name(), *path, Input::Unknown);
147        break;
148      }
149      default:
150        assert(0 && "Invalid script token in GROUP!");
151        break;
152    }  // end of switch
153
154    Input* input = *m_Builder.getCurrentNode();
155    assert(input != NULL);
156    if (!m_Builder.setMemory(*input, FileHandle::OpenMode(FileHandle::ReadOnly),
157                             FileHandle::Permission(FileHandle::System))) {
158      error(diag::err_cannot_open_input) << input->name() << input->path();
159    }
160    m_Builder.setContext(*input);
161  }
162
163  // --end-group
164  m_Builder.exitGroup();
165
166  // read the group
167  m_GroupReader.readGroup(group, m_InputTree.end(), m_Builder, m_Config);
168}
169
170}  // namespace mcld
171