|
| 1 | +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +use driver::session; |
| 12 | +use driver::session::Session; |
| 13 | +use syntax::parse::token::special_idents; |
| 14 | +use syntax::ast::{crate, node_id, item, item_fn}; |
| 15 | +use syntax::codemap::span; |
| 16 | +use syntax::visit::{default_visitor, mk_vt, vt, Visitor, visit_crate, visit_item}; |
| 17 | +use syntax::attr::{attrs_contains_name}; |
| 18 | +use syntax::ast_map; |
| 19 | +use core::util; |
| 20 | + |
| 21 | +struct EntryContext { |
| 22 | + session: Session, |
| 23 | + |
| 24 | + ast_map: ast_map::map, |
| 25 | + |
| 26 | + // The top-level function called 'main' |
| 27 | + main_fn: Option<(node_id, span)>, |
| 28 | + |
| 29 | + // The function that has attribute named 'main' |
| 30 | + attr_main_fn: Option<(node_id, span)>, |
| 31 | + |
| 32 | + // The function that has the attribute 'start' on it |
| 33 | + start_fn: Option<(node_id, span)>, |
| 34 | + |
| 35 | + // The functions that one might think are 'main' but aren't, e.g. |
| 36 | + // main functions not defined at the top level. For diagnostics. |
| 37 | + non_main_fns: ~[(node_id, span)], |
| 38 | +} |
| 39 | + |
| 40 | +type EntryVisitor = vt<@mut EntryContext>; |
| 41 | + |
| 42 | +pub fn find_entry_point(session: Session, crate: @crate, ast_map: ast_map::map) { |
| 43 | + |
| 44 | + // FIXME #4404 android JNI hacks |
| 45 | + if *session.building_library && |
| 46 | + session.targ_cfg.os != session::os_android { |
| 47 | + // No need to find a main function |
| 48 | + return; |
| 49 | + } |
| 50 | + |
| 51 | + let ctxt = @mut EntryContext { |
| 52 | + session: session, |
| 53 | + ast_map: ast_map, |
| 54 | + main_fn: None, |
| 55 | + attr_main_fn: None, |
| 56 | + start_fn: None, |
| 57 | + non_main_fns: ~[], |
| 58 | + }; |
| 59 | + |
| 60 | + visit_crate(crate, ctxt, mk_vt(@Visitor { |
| 61 | + visit_item: |item, ctxt, visitor| find_item(item, ctxt, visitor), |
| 62 | + .. *default_visitor() |
| 63 | + })); |
| 64 | + |
| 65 | + configure_main(ctxt); |
| 66 | +} |
| 67 | + |
| 68 | +fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { |
| 69 | + match item.node { |
| 70 | + item_fn(*) => { |
| 71 | + if item.ident == special_idents::main { |
| 72 | + match ctxt.ast_map.find(&item.id) { |
| 73 | + Some(&ast_map::node_item(_, path)) => { |
| 74 | + if path.len() == 0 { |
| 75 | + // This is a top-level function so can be 'main' |
| 76 | + if ctxt.main_fn.is_none() { |
| 77 | + ctxt.main_fn = Some((item.id, item.span)); |
| 78 | + } else { |
| 79 | + ctxt.session.span_err( |
| 80 | + item.span, |
| 81 | + ~"multiple 'main' functions"); |
| 82 | + } |
| 83 | + } else { |
| 84 | + // This isn't main |
| 85 | + ctxt.non_main_fns.push((item.id, item.span)); |
| 86 | + } |
| 87 | + } |
| 88 | + _ => util::unreachable() |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + if attrs_contains_name(item.attrs, ~"main") { |
| 93 | + if ctxt.attr_main_fn.is_none() { |
| 94 | + ctxt.attr_main_fn = Some((item.id, item.span)); |
| 95 | + } else { |
| 96 | + ctxt.session.span_err( |
| 97 | + item.span, |
| 98 | + ~"multiple 'main' functions"); |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + if attrs_contains_name(item.attrs, ~"start") { |
| 103 | + if ctxt.start_fn.is_none() { |
| 104 | + ctxt.start_fn = Some((item.id, item.span)); |
| 105 | + } else { |
| 106 | + ctxt.session.span_err( |
| 107 | + item.span, |
| 108 | + ~"multiple 'start' functions"); |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + _ => () |
| 113 | + } |
| 114 | + |
| 115 | + visit_item(item, ctxt, visitor); |
| 116 | +} |
| 117 | + |
| 118 | +fn configure_main(ctxt: @mut EntryContext) { |
| 119 | + let this = &mut *ctxt; |
| 120 | + if this.start_fn.is_some() { |
| 121 | + *this.session.entry_fn = this.start_fn; |
| 122 | + *this.session.entry_type = Some(session::EntryStart); |
| 123 | + } else if this.attr_main_fn.is_some() { |
| 124 | + *this.session.entry_fn = this.attr_main_fn; |
| 125 | + *this.session.entry_type = Some(session::EntryMain); |
| 126 | + } else if this.main_fn.is_some() { |
| 127 | + *this.session.entry_fn = this.main_fn; |
| 128 | + *this.session.entry_type = Some(session::EntryMain); |
| 129 | + } else { |
| 130 | + if !*this.session.building_library { |
| 131 | + // No main function |
| 132 | + this.session.err(~"main function not found"); |
| 133 | + if !this.non_main_fns.is_empty() { |
| 134 | + // There were some functions named 'main' though. Try to give the user a hint. |
| 135 | + this.session.note(~"the main function must be defined at the crate level \ |
| 136 | + but you have one or more functions named 'main' that are not \ |
| 137 | + defined at the crate level. Either move the definition or \ |
| 138 | + attach the `#[main]` attribute to override this behavior."); |
| 139 | + for this.non_main_fns.each |&(_, span)| { |
| 140 | + this.session.span_note(span, ~"here is a function named 'main'"); |
| 141 | + } |
| 142 | + } |
| 143 | + this.session.abort_if_errors(); |
| 144 | + } else { |
| 145 | + // If we *are* building a library, then we're on android where we still might |
| 146 | + // optionally want to translate main $4404 |
| 147 | + assert!(this.session.targ_cfg.os == session::os_android); |
| 148 | + } |
| 149 | + } |
| 150 | +} |
0 commit comments