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