Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Crystal.main #4998

Merged
merged 2 commits into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions samples/sdl/sdl/lib_sdl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,3 @@ lib LibSDL
fun get_ticks = SDL_GetTicks : UInt32
fun flip = SDL_Flip(screen : Surface*) : Int32
end

{% if flag?(:darwin) %}
redefine_main(SDL_main) do |main|
\{{main}}
end
{% end %}
7 changes: 3 additions & 4 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ module Crystal
end
end

def codegen(node, single_module = false, debug = Debug::Default, expose_crystal_main = true)
visitor = CodeGenVisitor.new self, node, single_module: single_module, debug: debug, expose_crystal_main: expose_crystal_main
def codegen(node, single_module = false, debug = Debug::Default)
visitor = CodeGenVisitor.new self, node, single_module: single_module, debug: debug
node.accept visitor
visitor.process_finished_hooks
visitor.finish
Expand Down Expand Up @@ -141,7 +141,7 @@ module Crystal
@main_module_info : ModuleInfo
@main_builder : CrystalLLVMBuilder

def initialize(@program : Program, @node : ASTNode, single_module = false, @debug = Debug::Default, expose_crystal_main = true)
def initialize(@program : Program, @node : ASTNode, single_module = false, @debug = Debug::Default)
@single_module = !!single_module
@abi = @program.target_machine.abi
@llvm_context = LLVM::Context.new
Expand All @@ -155,7 +155,6 @@ module Crystal
@main_ret_type = node.type? || @program.nil_type
ret_type = @llvm_typer.llvm_return_type(@main_ret_type)
@main = @llvm_mod.functions.add(MAIN_NAME, [llvm_context.int32, llvm_context.void_pointer.pointer], ret_type)
@main.linkage = LLVM::Linkage::Internal unless expose_crystal_main

emit_main_def_debug_metadata(@main, "??") unless @debug.none?

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ module Crystal

private def codegen(program, node : ASTNode, sources, output_filename)
llvm_modules = @progress_tracker.stage("Codegen (crystal)") do
program.codegen node, debug: debug, single_module: @single_module || @release || @cross_compile || @emit, expose_crystal_main: false
program.codegen node, debug: debug, single_module: @single_module || @release || @cross_compile || @emit
end

if @cross_compile
Expand Down
112 changes: 112 additions & 0 deletions src/crystal/main.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
lib LibCrystalMain
@[Raises]
fun __crystal_main(argc : Int32, argv : UInt8**)
end

# :nodoc:
def _crystal_main(argc : Int32, argv : UInt8**)
# TODO: remove this method and embed this inside
# Crystal.main. A bug in Crystal 0.23.1 prevents invoking
# __crystal_main from anywhere except the top level.
LibCrystalMain.__crystal_main(argc, argv)
end

module Crystal
# Defines the main routine run by normal Crystal programs:
#
# - Initializes the GC
# - Invokes the given *block*
# - Handles unhandled exceptions
# - Invokes `at_exit` handlers
# - Flushes `STDOUT` and `STDERR`
#
# This method can be invoked if you need to define a custom
# main (as in C main) function, doing all the above steps.
#
# For example:
#
# ```
# fun main(argc : Int32, argv : UInt8**) : Int32
# Crystal.main do
# time = Time.now
# Crystal.main_user_code(argc, argv)
# puts "Time to execute program: #{Time.now - time}"
# end
# end
# ```
#
# Note that the above is really just an example, almost the
# same can be accomplished with `at_exit`. But in some cases
# redefinition of C's main is needed.
def self.main(&block)
GC.init

status =
begin
yield
0
rescue ex
1
end

AtExitHandlers.run status
ex.inspect_with_backtrace STDERR if ex
STDOUT.flush
STDERR.flush
status
end

# Main method run by all Crystal programs at startup.
#
# This setups up the GC, invokes your program, rescuing
# any handled exception, and then runs `at_exit` handlers.
#
# This method is automatically invoked for you, so you
# don't need to invoke it.
#
# However, if you need to define a special main C function,
# you can redefine main and invoke `Crystal.main` from it:
#
# ```
# fun main(argc : Int32, argv : UInt8**) : Int32
# # some setup before Crystal main
# Crystal.main(argc, argv)
# # some cleanup logic after Crystal main
# end
# ```
#
# The `Crystal.main` can also be passed as a callback:
#
# ```
# fun main(argc : Int32, argv : UInt8**) : Int32
# LibFoo.init_foo_and_invoke_main(argc, argv, ->Crystal.main)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't ->Crystal.main be ->Crystal.main(Int32, UInt8**)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's a lib call the compiler figures out the argument types.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't know that, cushtie!

# end
# ```
#
# Note that before `Crystal.main` is invoked the GC
# is not setup yet, so nothing that allocates memory
# in Crystal (like `new` for classes) can be used.
def self.main(argc : Int32, argv : UInt8**)
main do
main_user_code(argc, argv)
end
end

# Executes the main user code. This normally is executed
# after initializing the GC and before executing `at_exit` handlers.
#
# You should never invoke this method unless you need to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to raise early if this is executed twice? Seems like a bit of an edge case but might be worth protecting. This could be another PR though.

# redefine C's main function. See `Crystal.main` for
# more details.
def self.main_user_code(argc : Int32, argv : UInt8**)
_crystal_main(argc, argv)
end
end

# Main function that acts as C's main function.
# Invokes `Crystal.main`.
#
# Can be redefined. See `Crystal.main` for examples.
fun main(argc : Int32, argv : UInt8**) : Int32
Crystal.main(argc, argv)
end
29 changes: 0 additions & 29 deletions src/main.cr

This file was deleted.

2 changes: 1 addition & 1 deletion src/prelude.cr
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ require "char"
require "char/reader"
require "class"
require "concurrent"
require "crystal/main"
require "deque"
require "dir"
require "enum"
Expand All @@ -45,7 +46,6 @@ require "int"
require "intrinsics"
require "io"
require "kernel"
require "main"
require "math/math"
require "mutex"
require "named_tuple"
Expand Down