startup-time 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +17 -12
- data/lib/startup_time/app.rb +64 -43
- data/lib/startup_time/builder.rb +137 -93
- data/lib/startup_time/options.rb +1 -1
- data/lib/startup_time/refinements.rb +23 -0
- data/lib/startup_time/version.rb +1 -1
- data/resources/src/hello.wren +1 -0
- data/resources/tests.yaml +12 -1
- metadata +5 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: fb5446ede5073351faeb7fee768f11d0bbbf8aa223cf5ad8f69b7782b63f91e3
         | 
| 4 | 
            +
              data.tar.gz: 699c670e0386d4701a589d25e4467e3f36ad8c6d9baf2c49fc1b4819b35318c5
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c7f577bdf5e4d5782825efe6562e97b2cc3210fd0471faf61fc052f1fc6ed6f798b93d8349a13cd652953f68a69096c2fe9cd45109ba3d3f15bb81207d3360a6
         | 
| 7 | 
            +
              data.tar.gz: a8a74489a53efc48456fd20af5533e716fbef5731a1b853fefd1b9565734c07ea8e8b1ae28ac065862aec9a4ad5b05e06364941575f2dfc2f17a8963bf6067aa
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,10 @@ | |
| 1 | 
            +
            ## 1.3.0 - 2019-09-11
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            - bump the default minimum duration from 5s to 10s
         | 
| 4 | 
            +
            - disable the Java tests (javac, java-native) if `java` is installed but
         | 
| 5 | 
            +
              `javac` isn't
         | 
| 6 | 
            +
            - add [Wren](http://wren.io/)
         | 
| 7 | 
            +
             | 
| 1 8 | 
             
            ## 1.2.0 - 2019-07-15
         | 
| 2 9 |  | 
| 3 10 | 
             
            - add -t/--time option specifying the minimum length of time to run tests for
         | 
| @@ -6,7 +13,7 @@ | |
| 6 13 |  | 
| 7 14 | 
             
            ## 1.1.1 - 2019-07-13
         | 
| 8 15 |  | 
| 9 | 
            -
            - add QuickJS
         | 
| 16 | 
            +
            - add [QuickJS](https://bellard.org/quickjs/)
         | 
| 10 17 | 
             
            - update deno version command
         | 
| 11 18 |  | 
| 12 19 | 
             
            ## 1.1.0 - 2019-02-27
         | 
    
        data/README.md
    CHANGED
    
    | @@ -49,8 +49,11 @@ $ startup-time --only jvm | |
| 49 49 | 
             
            # only run tests which finish quickly
         | 
| 50 50 | 
             
            $ startup-time --only fast --omit slow-compile
         | 
| 51 51 |  | 
| 52 | 
            -
            #  | 
| 52 | 
            +
            # run the test suite for at least 10 seconds (default: 5)
         | 
| 53 53 | 
             
            $ startup-time --time 10
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            # run each program 10 times
         | 
| 56 | 
            +
            $ startup-time --count 10
         | 
| 54 57 | 
             
            ```
         | 
| 55 58 |  | 
| 56 59 | 
             
            ### Sample Output
         | 
| @@ -62,24 +65,25 @@ $ startup-time --time 10 | |
| 62 65 | 
             
                LuaJIT                     0.64
         | 
| 63 66 | 
             
                Go                         0.66
         | 
| 64 67 | 
             
                Rust                       0.67
         | 
| 68 | 
            +
                JavaScript (QuickJS)       0.83
         | 
| 65 69 | 
             
                D (DMD)                    0.88
         | 
| 66 | 
            -
                 | 
| 67 | 
            -
                 | 
| 70 | 
            +
                Lua                        0.94
         | 
| 71 | 
            +
                Java Native (GraalVM)      0.99
         | 
| 68 72 | 
             
                D (GDC)                    1.10
         | 
| 69 73 | 
             
                Haskell (GHC)              1.14
         | 
| 70 | 
            -
                 | 
| 71 | 
            -
                 | 
| 74 | 
            +
                Wren                       1.14
         | 
| 75 | 
            +
                C++ (g++)                  1.19
         | 
| 76 | 
            +
                Crystal                    1.55
         | 
| 72 77 | 
             
                Perl                       1.66
         | 
| 73 | 
            -
                 | 
| 74 | 
            -
                 | 
| 78 | 
            +
                Bash                       2.63
         | 
| 79 | 
            +
                JavaScript (GraalVM)       3.88
         | 
| 75 80 | 
             
                JavaScript (Deno)         11.15
         | 
| 76 81 | 
             
                Python 3                  22.09
         | 
| 77 | 
            -
                 | 
| 82 | 
            +
                Ruby (TruffleRuby)        33.59
         | 
| 78 83 | 
             
                JavaScript (Node.js)      35.20
         | 
| 79 84 | 
             
                Python 2                  43.43
         | 
| 80 85 | 
             
                Java                      54.59
         | 
| 81 | 
            -
                Ruby                       | 
| 82 | 
            -
                Ruby (TruffleRuby)        81.77
         | 
| 86 | 
            +
                Ruby                      68.68
         | 
| 83 87 | 
             
                Kotlin                   103.02
         | 
| 84 88 | 
             
                Scala                    801.21
         | 
| 85 89 |  | 
| @@ -118,7 +122,7 @@ OPTIONS: | |
| 118 122 | 
             
                -O, --omit LIST                  Don't run the specified tests (comma-separated list of IDs/groups)
         | 
| 119 123 | 
             
                -q, --quiet                      Suppress all inessential output
         | 
| 120 124 | 
             
                -t, --time INTEGER               The minimum number of seconds to run the test suite for
         | 
| 121 | 
            -
                                                 (minimum: 2, default:  | 
| 125 | 
            +
                                                 (minimum: 2, default: 10)
         | 
| 122 126 | 
             
                -v, --verbose                    Enable verbose logging
         | 
| 123 127 | 
             
                -V, --version                    Display the version and exit
         | 
| 124 128 | 
             
            ```
         | 
| @@ -134,6 +138,7 @@ OPTIONS: | |
| 134 138 |  | 
| 135 139 | 
             
            ## SEE ALSO
         | 
| 136 140 |  | 
| 141 | 
            +
            - [Gabriel439/bench](https://github.com/Gabriel439/bench) - a command-line benchmark tool written in Haskell
         | 
| 137 142 | 
             
            - [gnustavo/startup-times](https://github.com/gnustavo/startup-times) - a script to investigate the startup times of several programming languages
         | 
| 138 143 | 
             
            - [jwiegley/helloworld](https://github.com/jwiegley/helloworld) - a comparison of "Hello, world" startup times in various languages
         | 
| 139 144 | 
             
            - [sharkdp/hyperfine](https://github.com/sharkdp/hyperfine) - a command-line benchmarking tool
         | 
| @@ -144,7 +149,7 @@ OPTIONS: | |
| 144 149 |  | 
| 145 150 | 
             
            ## VERSION
         | 
| 146 151 |  | 
| 147 | 
            -
            1. | 
| 152 | 
            +
            1.3.0
         | 
| 148 153 |  | 
| 149 154 | 
             
            ## COPYRIGHT AND LICENSE
         | 
| 150 155 |  | 
    
        data/lib/startup_time/app.rb
    CHANGED
    
    | @@ -64,35 +64,9 @@ module StartupTime | |
| 64 64 | 
             
                def benchmark
         | 
| 65 65 | 
             
                  builder.build!
         | 
| 66 66 |  | 
| 67 | 
            -
                   | 
| 68 | 
            -
                  #
         | 
| 69 | 
            -
                  #   - its interpreter exists
         | 
| 70 | 
            -
                  #   - it's a compiled executable (i.e. its compiler exists)
         | 
| 71 | 
            -
                  #
         | 
| 72 | 
            -
                  # otherwise, skip it
         | 
| 73 | 
            -
                  runnable_tests = selected_tests.each_with_object([]) do |(id, test), tests|
         | 
| 74 | 
            -
                    args = Array(test[:command])
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                    if args.length == 1 # native executable
         | 
| 77 | 
            -
                      compiler = test[:compiler] || id
         | 
| 78 | 
            -
                      path = File.absolute_path(args.first)
         | 
| 79 | 
            -
                      next unless File.exist?(path)
         | 
| 80 | 
            -
                    else # interpreter + source
         | 
| 81 | 
            -
                      compiler = args.first
         | 
| 82 | 
            -
                      path = which(compiler)
         | 
| 83 | 
            -
                      next unless path
         | 
| 84 | 
            -
                    end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                    tests << {
         | 
| 87 | 
            -
                      id: id,
         | 
| 88 | 
            -
                      test: test,
         | 
| 89 | 
            -
                      args: args,
         | 
| 90 | 
            -
                      compiler: compiler,
         | 
| 91 | 
            -
                      path: path,
         | 
| 92 | 
            -
                    }
         | 
| 93 | 
            -
                  end
         | 
| 67 | 
            +
                  tests = runnable_tests
         | 
| 94 68 |  | 
| 95 | 
            -
                  if  | 
| 69 | 
            +
                  if tests.empty?
         | 
| 96 70 | 
             
                    puts '[]' if @json
         | 
| 97 71 | 
             
                    return
         | 
| 98 72 | 
             
                  end
         | 
| @@ -100,12 +74,11 @@ module StartupTime | |
| 100 74 | 
             
                  spec = @options.spec
         | 
| 101 75 |  | 
| 102 76 | 
             
                  if spec.type == :duration
         | 
| 103 | 
            -
                    spec = spec.with(value: spec.value.to_f /  | 
| 77 | 
            +
                    spec = spec.with(value: spec.value.to_f / tests.length)
         | 
| 104 78 | 
             
                  end
         | 
| 105 79 |  | 
| 106 | 
            -
                   | 
| 107 | 
            -
                     | 
| 108 | 
            -
                    time(config)
         | 
| 80 | 
            +
                  tests.shuffle.each do |test|
         | 
| 81 | 
            +
                    time(spec, test)
         | 
| 109 82 | 
             
                  end
         | 
| 110 83 |  | 
| 111 84 | 
             
                  sorted = @times.sort_by { |result| result[:time] }
         | 
| @@ -131,9 +104,54 @@ module StartupTime | |
| 131 104 | 
             
                  end
         | 
| 132 105 | 
             
                end
         | 
| 133 106 |  | 
| 107 | 
            +
                # return the subset of selected tests that are runnable.
         | 
| 108 | 
            +
                #
         | 
| 109 | 
            +
                # a test is runnable if these requirements are met:
         | 
| 110 | 
            +
                #
         | 
| 111 | 
            +
                #   - (if it's interpreted): the interpreter exists
         | 
| 112 | 
            +
                #   - (if it's compiled): the compiler exists
         | 
| 113 | 
            +
                #
         | 
| 114 | 
            +
                # otherwise, skip it
         | 
| 115 | 
            +
                #
         | 
| 116 | 
            +
                # the compiler path is resolved in the builder, which can also disable a test
         | 
| 117 | 
            +
                # (by setting test[:disabled] = true) if the compilation prerequisites aren't
         | 
| 118 | 
            +
                # installed
         | 
| 119 | 
            +
                #
         | 
| 120 | 
            +
                # note that some tests are both interpreted and compiled, so both the
         | 
| 121 | 
            +
                # compiler and the interpreter must exist, e.g.:
         | 
| 122 | 
            +
                #
         | 
| 123 | 
            +
                #   - interpreter: java
         | 
| 124 | 
            +
                #   - compiler: javac
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def runnable_tests
         | 
| 127 | 
            +
                  selected_tests.each_with_object([]) do |(id, test), tests|
         | 
| 128 | 
            +
                    next if test[:disabled]
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    args = Array(test[:command])
         | 
| 131 | 
            +
                    compiler = test[:compiler]
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    if args.length == 1 # native executable
         | 
| 134 | 
            +
                      executable = File.absolute_path(args.first)
         | 
| 135 | 
            +
                      next unless File.exist?(executable)
         | 
| 136 | 
            +
                    else # interpreter + source/bytecode
         | 
| 137 | 
            +
                      executable = which(args.first)
         | 
| 138 | 
            +
                      next unless (interpreter = executable)
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    tests << {
         | 
| 142 | 
            +
                      args:        args,
         | 
| 143 | 
            +
                      compiler:    compiler,
         | 
| 144 | 
            +
                      executable:  executable,
         | 
| 145 | 
            +
                      id:          id,
         | 
| 146 | 
            +
                      interpreter: interpreter,
         | 
| 147 | 
            +
                      test:        test,
         | 
| 148 | 
            +
                    }
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
             | 
| 134 152 | 
             
                # takes a test configuration and measures how long it takes to execute the
         | 
| 135 153 | 
             
                # test
         | 
| 136 | 
            -
                def time( | 
| 154 | 
            +
                def time(spec, args:, compiler:, executable:, id:, interpreter:, test:)
         | 
| 137 155 | 
             
                  # dump the compiler/interpreter's version if running in verbose mode
         | 
| 138 156 | 
             
                  if @verbosity == :verbose
         | 
| 139 157 | 
             
                    puts
         | 
| @@ -144,19 +162,22 @@ module StartupTime | |
| 144 162 | 
             
                    version = test[:version]
         | 
| 145 163 |  | 
| 146 164 | 
             
                    unless version == false
         | 
| 147 | 
            -
                       | 
| 165 | 
            +
                      # if the test is both interpreted and compiled, default to
         | 
| 166 | 
            +
                      # dumping the compiler version
         | 
| 167 | 
            +
                      version ||= '%{compiler} --version' if compiler
         | 
| 168 | 
            +
                      version ||= '%{interpreter} --version' if interpreter
         | 
| 148 169 |  | 
| 149 | 
            -
                       | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
                       | 
| 170 | 
            +
                      version_command = version % {
         | 
| 171 | 
            +
                        compiler: compiler || interpreter,
         | 
| 172 | 
            +
                        interpreter: interpreter,
         | 
| 173 | 
            +
                      }
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                      sh version_command
         | 
| 155 176 | 
             
                    end
         | 
| 156 177 | 
             
                  end
         | 
| 157 178 |  | 
| 158 179 | 
             
                  argv0 = args.shift
         | 
| 159 | 
            -
                  command = [ | 
| 180 | 
            +
                  command = [executable, *args]
         | 
| 160 181 |  | 
| 161 182 | 
             
                  unless @verbosity == :quiet
         | 
| 162 183 | 
             
                    if @verbosity == :verbose
         | 
| @@ -190,7 +211,7 @@ module StartupTime | |
| 190 211 |  | 
| 191 212 | 
             
                      loop do
         | 
| 192 213 | 
             
                        time = Benchmark.realtime do
         | 
| 193 | 
            -
                          system([ | 
| 214 | 
            +
                          system([executable, argv0], *args, out: File::NULL)
         | 
| 194 215 | 
             
                        end
         | 
| 195 216 |  | 
| 196 217 | 
             
                        elapsed = Time.now - start
         | 
| @@ -201,7 +222,7 @@ module StartupTime | |
| 201 222 | 
             
                    else # how many times to run the tests
         | 
| 202 223 | 
             
                      spec.value.times do
         | 
| 203 224 | 
             
                        times << Benchmark.realtime do
         | 
| 204 | 
            -
                          system([ | 
| 225 | 
            +
                          system([executable, argv0], *args, out: File::NULL)
         | 
| 205 226 | 
             
                        end
         | 
| 206 227 | 
             
                      end
         | 
| 207 228 | 
             
                    end
         | 
    
        data/lib/startup_time/builder.rb
    CHANGED
    
    | @@ -1,9 +1,14 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'rake'
         | 
| 4 | 
            +
            require 'set'
         | 
| 4 5 | 
             
            require 'shellwords'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative 'refinements'
         | 
| 5 8 | 
             
            require_relative 'util'
         | 
| 6 9 |  | 
| 10 | 
            +
            using StartupTime::Refinements
         | 
| 11 | 
            +
             | 
| 7 12 | 
             
            module StartupTime
         | 
| 8 13 | 
             
              # StartupTime::Builder - clean and prepare the build directory
         | 
| 9 14 | 
             
              #
         | 
| @@ -16,6 +21,11 @@ module StartupTime | |
| 16 21 | 
             
              # once these tasks are complete, everything required to run the benchmark tests
         | 
| 17 22 | 
             
              # will be available in the build directory
         | 
| 18 23 | 
             
              class Builder
         | 
| 24 | 
            +
                CUSTOM_COMPILER = {
         | 
| 25 | 
            +
                  'kotlinc-native': :compile_kotlinc_native,
         | 
| 26 | 
            +
                  'java-native': :compile_java_native,
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
             | 
| 19 29 | 
             
                SRC_DIR = File.absolute_path('../../resources/src', __dir__)
         | 
| 20 30 |  | 
| 21 31 | 
             
                include Rake::DSL
         | 
| @@ -23,17 +33,12 @@ module StartupTime | |
| 23 33 | 
             
                include Services.mixin %i[options selected_tests]
         | 
| 24 34 |  | 
| 25 35 | 
             
                def initialize
         | 
| 26 | 
            -
                  @verbosity = options.verbosity
         | 
| 27 36 | 
             
                  @build_dir = options.build_dir
         | 
| 37 | 
            +
                  @verbosity = options.verbosity
         | 
| 28 38 |  | 
| 29 39 | 
             
                  Rake.verbose(@verbosity != :quiet)
         | 
| 30 40 | 
             
                end
         | 
| 31 41 |  | 
| 32 | 
            -
                # remove the build directory and its contents
         | 
| 33 | 
            -
                def clean!
         | 
| 34 | 
            -
                  rm_rf @build_dir
         | 
| 35 | 
            -
                end
         | 
| 36 | 
            -
             | 
| 37 42 | 
             
                # ensure the build directory is in a fit state to run the tests i.e. copy
         | 
| 38 43 | 
             
                # source files and compile target files
         | 
| 39 44 | 
             
                def build!
         | 
| @@ -47,27 +52,13 @@ module StartupTime | |
| 47 52 | 
             
                  Rake::Task[:build].invoke
         | 
| 48 53 | 
             
                end
         | 
| 49 54 |  | 
| 50 | 
            -
                 | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
                # which allows the command's environment to be included in the final options
         | 
| 54 | 
            -
                # hash rather than cramming it in as the first argument i.e.:
         | 
| 55 | 
            -
                #
         | 
| 56 | 
            -
                # before:
         | 
| 57 | 
            -
                #
         | 
| 58 | 
            -
                #   sh FOO_VERBOSE: "0", "foo -c hello.foo -o hello", out: File::NULL
         | 
| 59 | 
            -
                #
         | 
| 60 | 
            -
                # after:
         | 
| 61 | 
            -
                #
         | 
| 62 | 
            -
                #   shell "foo -c hello.foo -o hello", env: { FOO_VERBOSE: "0" }, out: File::NULL
         | 
| 63 | 
            -
                #
         | 
| 64 | 
            -
                def shell(args, **options)
         | 
| 65 | 
            -
                  args = Array(args) # args is a string or array
         | 
| 66 | 
            -
                  env = options.delete(:env)
         | 
| 67 | 
            -
                  args.unshift(env) if env
         | 
| 68 | 
            -
                  sh(*args, options)
         | 
| 55 | 
            +
                # remove the build directory and its contents
         | 
| 56 | 
            +
                def clean!
         | 
| 57 | 
            +
                  rm_rf @build_dir
         | 
| 69 58 | 
             
                end
         | 
| 70 59 |  | 
| 60 | 
            +
                private
         | 
| 61 | 
            +
             | 
| 71 62 | 
             
                # a conditional version of Rake's `file` task which compiles a source file to
         | 
| 72 63 | 
             
                # a target file via the block provided. if the compiler isn't installed, the
         | 
| 73 64 | 
             
                # task is skipped.
         | 
| @@ -88,6 +79,12 @@ module StartupTime | |
| 88 79 |  | 
| 89 80 | 
             
                  return unless (compiler_path = which(compiler))
         | 
| 90 81 |  | 
| 82 | 
            +
                  # update the test spec's compiler field to point to the compiler's
         | 
| 83 | 
            +
                  # absolute path
         | 
| 84 | 
            +
                  #
         | 
| 85 | 
            +
                  # XXX mutation/side-effect
         | 
| 86 | 
            +
                  test[:compiler] = compiler_path
         | 
| 87 | 
            +
             | 
| 91 88 | 
             
                  # the source filename must be supplied
         | 
| 92 89 | 
             
                  source = test.fetch(:source)
         | 
| 93 90 |  | 
| @@ -97,17 +94,13 @@ module StartupTime | |
| 97 94 |  | 
| 98 95 | 
             
                    if command.length == 1
         | 
| 99 96 | 
             
                      target = command.first
         | 
| 100 | 
            -
                    elsif source.match?( | 
| 97 | 
            +
                    elsif source.match?(/\A[A-Z]/) # JVM language
         | 
| 101 98 | 
             
                      target = source.pathmap('%n.class')
         | 
| 102 99 | 
             
                    else # native executable
         | 
| 103 100 | 
             
                      target = '%s.out' % source
         | 
| 104 101 | 
             
                    end
         | 
| 105 102 | 
             
                  end
         | 
| 106 103 |  | 
| 107 | 
            -
                  # update the test spec's compiler field to point to the compiler's
         | 
| 108 | 
            -
                  # absolute path (which may be mocked)
         | 
| 109 | 
            -
                  test = test.merge(compiler: compiler_path)
         | 
| 110 | 
            -
             | 
| 111 104 | 
             
                  # pass the test object as the `file(...) { ... }` block's second
         | 
| 112 105 | 
             
                  # argument. Rake passes an instance of +Rake::TaskArguments+, a Hash-like
         | 
| 113 106 | 
             
                  # object which provides access to the command-line arguments for a Rake
         | 
| @@ -119,22 +112,96 @@ module StartupTime | |
| 119 112 | 
             
                  # declare the prerequisites for the target file.
         | 
| 120 113 | 
             
                  # compiler_path: recompile if the compiler has been
         | 
| 121 114 | 
             
                  # updated since the target was last built
         | 
| 122 | 
            -
                  file(target => [source, compiler_path], &wrapper)
         | 
| 115 | 
            +
                  file_task = file(target => [source, compiler_path], &wrapper)
         | 
| 123 116 |  | 
| 124 | 
            -
                  #  | 
| 125 | 
            -
                   | 
| 117 | 
            +
                  # register the task under the supplied ID so it can be referenced by name
         | 
| 118 | 
            +
                  # rather than by filename
         | 
| 119 | 
            +
                  compile_task = task(id => file_task)
         | 
| 126 120 |  | 
| 127 | 
            -
                  target
         | 
| 121 | 
            +
                  # add the task which builds the target file to the build task as a
         | 
| 122 | 
            +
                  # prerequisite
         | 
| 123 | 
            +
                  # task(:build => target) unless options[:connect] == false
         | 
| 124 | 
            +
                  task(:build => compile_task) unless options[:connect] == false
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  compile_task
         | 
| 128 127 | 
             
                end
         | 
| 129 128 |  | 
| 130 | 
            -
                #  | 
| 131 | 
            -
                #
         | 
| 132 | 
            -
                #  | 
| 133 | 
            -
                 | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 129 | 
            +
                # native-image compiles .class files to native binaries. it differs from
         | 
| 130 | 
            +
                # the other tasks because it depends on a target file rather than a
         | 
| 131 | 
            +
                # source file i.e. it depends on the target of the javac task
         | 
| 132 | 
            +
                def compile_java_native
         | 
| 133 | 
            +
                  java_native = compile_if('java-native', connect: false) do |t, test|
         | 
| 134 | 
            +
                    # XXX native-image doesn't provide a way to silence its output, so
         | 
| 135 | 
            +
                    # send it to /dev/null
         | 
| 136 | 
            +
                    shell [test[:compiler], "-H:Name=#{t.target}", '--no-server', '-O1', t.source.ext], {
         | 
| 137 | 
            +
                      out: File::NULL
         | 
| 138 | 
            +
                    }
         | 
| 139 | 
            +
                  end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  return unless java_native # return a falsey value i.e. disable the test
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                  javac = Rake.application.lookup(:javac) || begin
         | 
| 144 | 
            +
                    compile_if(:javac, connect: false, force: true) do |task, test|
         | 
| 145 | 
            +
                      run(test[:compile], task, test)
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
                  end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                  return unless javac # disable this test if javac is not available
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                  # prepend the javac task to this task as a prerequisite
         | 
| 152 | 
            +
                  java_native.prepend(javac)
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                  # register this task as a dependency of the root (:build) task
         | 
| 155 | 
            +
                  task(:build => java_native)
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  # uncomment this to see the dependency graph
         | 
| 158 | 
            +
                  # pp Rake::Task.tasks
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                  java_native
         | 
| 161 | 
            +
                end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                # implement the compilation step for the kotlinc-native test manually. we
         | 
| 164 | 
            +
                # need to do this to work around the compiler's non-standard behavior
         | 
| 165 | 
            +
                def compile_kotlinc_native
         | 
| 166 | 
            +
                  compile_if 'kotlinc-native' do |t, test|
         | 
| 167 | 
            +
                    # XXX kotlinc-native doesn't provide a way to silence
         | 
| 168 | 
            +
                    # its debug messages, so file them under /dev/null
         | 
| 169 | 
            +
                    shell %W[#{test[:compiler]} -opt -o #{t.target} #{t.source}], out: File::NULL
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    # XXX work around a kotlinc-native "feature"
         | 
| 172 | 
            +
                    # https://github.com/JetBrains/kotlin-native/issues/967
         | 
| 173 | 
            +
                    exe = "#{t.target}.kexe" # XXX or .exe, or...
         | 
| 174 | 
            +
                    verbose(@verbosity == :verbose) { mv exe, t.target } if File.exist?(exe)
         | 
| 175 | 
            +
                  end
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                # make sure the target files (e.g. native executables and JVM .class files)
         | 
| 179 | 
            +
                # are built if their compilers are installed
         | 
| 180 | 
            +
                def compile_target_files
         | 
| 181 | 
            +
                  selected_tests.each do |id, test|
         | 
| 182 | 
            +
                    enabled = true
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                    # handle the tests which have compile templates by a) turning them into
         | 
| 185 | 
            +
                    # blocks which substitute the compiler, source file and target file into
         | 
| 186 | 
            +
                    # the corresponding placeholders in the template, then b) executing the
         | 
| 187 | 
            +
                    # resulting command via +shell+
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                    if (command = test[:compile])
         | 
| 190 | 
            +
                      block = ->(task, test_) { run(command, task, test_) }
         | 
| 191 | 
            +
                      enabled = compile_if(id, &block)
         | 
| 192 | 
            +
                    end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                    test[:disabled] = !enabled
         | 
| 195 | 
            +
                  end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                  # do these after the main pass so they can reuse tasks (if available)
         | 
| 198 | 
            +
                  # e.g. the javac task
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                  CUSTOM_COMPILER.each do |id, meth|
         | 
| 201 | 
            +
                    selected_tests[id].tap do |test|
         | 
| 202 | 
            +
                      test[:disabled] = !send(meth) if test
         | 
| 203 | 
            +
                    end
         | 
| 204 | 
            +
                  end
         | 
| 138 205 | 
             
                end
         | 
| 139 206 |  | 
| 140 207 | 
             
                # ensure each file in the source directory is mirrored to the build
         | 
| @@ -152,6 +219,16 @@ module StartupTime | |
| 152 219 | 
             
                  end
         | 
| 153 220 | 
             
                end
         | 
| 154 221 |  | 
| 222 | 
            +
                # register the prerequisites of the :build task. creates file tasks which:
         | 
| 223 | 
            +
                #
         | 
| 224 | 
            +
                # a) keep the build directory sources in sync with the source directory
         | 
| 225 | 
            +
                # b) rebuild target files if their source files are modified
         | 
| 226 | 
            +
                # c) rebuild target files if their compilers are updated
         | 
| 227 | 
            +
                def register_tasks
         | 
| 228 | 
            +
                  copy_source_files
         | 
| 229 | 
            +
                  compile_target_files
         | 
| 230 | 
            +
                end
         | 
| 231 | 
            +
             | 
| 155 232 | 
             
                # run a shell command (string) by substituting the compiler path, source
         | 
| 156 233 | 
             
                # file, and target file into the supplied template string and executing the
         | 
| 157 234 | 
             
                # resulting command with the test's (optional) environment hash
         | 
| @@ -159,63 +236,30 @@ module StartupTime | |
| 159 236 | 
             
                  replacements = {
         | 
| 160 237 | 
             
                    compiler: Shellwords.escape(test[:compiler]),
         | 
| 161 238 | 
             
                    source: Shellwords.escape(task.source),
         | 
| 162 | 
            -
                    target: Shellwords.escape(task. | 
| 239 | 
            +
                    target: Shellwords.escape(task.target),
         | 
| 163 240 | 
             
                  }
         | 
| 164 241 |  | 
| 165 242 | 
             
                  command = template % replacements
         | 
| 166 243 | 
             
                  shell(command, env: test[:env])
         | 
| 167 244 | 
             
                end
         | 
| 168 245 |  | 
| 169 | 
            -
                #  | 
| 170 | 
            -
                #  | 
| 171 | 
            -
                 | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
                   | 
| 184 | 
            -
                   | 
| 185 | 
            -
                   | 
| 186 | 
            -
                  java_native = compile_if('java-native', connect: false) do |t, test|
         | 
| 187 | 
            -
                    # XXX native-image doesn't provide a way to silence its output, so
         | 
| 188 | 
            -
                    # send it to /dev/null
         | 
| 189 | 
            -
                    shell [test[:compiler], "-H:Name=#{t.name}", '--no-server', '-O1', t.source.ext], {
         | 
| 190 | 
            -
                      out: File::NULL
         | 
| 191 | 
            -
                    }
         | 
| 192 | 
            -
                  end
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                  if java_native
         | 
| 195 | 
            -
                    javac = compile_if(:javac, connect: false, force: true) do |task, test|
         | 
| 196 | 
            -
                      run('%{compiler} -d . %{source}', task, test)
         | 
| 197 | 
            -
                    end
         | 
| 198 | 
            -
             | 
| 199 | 
            -
                    if javac
         | 
| 200 | 
            -
                      task java_native => javac
         | 
| 201 | 
            -
                      task :build => java_native
         | 
| 202 | 
            -
                    end
         | 
| 203 | 
            -
                  else
         | 
| 204 | 
            -
                    compile_if :javac do |task, test|
         | 
| 205 | 
            -
                      run('%{compiler} -d . %{source}', task, test)
         | 
| 206 | 
            -
                    end
         | 
| 207 | 
            -
                  end
         | 
| 208 | 
            -
             | 
| 209 | 
            -
                  compile_if 'kotlinc-native' do |t, test|
         | 
| 210 | 
            -
                    # XXX kotlinc-native doesn't provide a way to silence
         | 
| 211 | 
            -
                    # its debug messages, so file them under /dev/null
         | 
| 212 | 
            -
                    shell %W[#{test[:compiler]} -opt -o #{t.name} #{t.source}], out: File::NULL
         | 
| 213 | 
            -
             | 
| 214 | 
            -
                    # XXX work around a kotlinc-native "feature"
         | 
| 215 | 
            -
                    # https://github.com/JetBrains/kotlin-native/issues/967
         | 
| 216 | 
            -
                    exe = "#{t.name}.kexe" # XXX or .exe, or...
         | 
| 217 | 
            -
                    verbose(@verbosity == :verbose) { mv exe, t.name } if File.exist?(exe)
         | 
| 218 | 
            -
                  end
         | 
| 246 | 
            +
                # a wrapper for Rake's +FileUtils#sh+ method (which wraps +Kernel#spawn+)
         | 
| 247 | 
            +
                # which allows the command's environment to be included in the final options
         | 
| 248 | 
            +
                # hash rather than cramming it in as the first argument i.e.:
         | 
| 249 | 
            +
                #
         | 
| 250 | 
            +
                # before:
         | 
| 251 | 
            +
                #
         | 
| 252 | 
            +
                #   sh FOO_VERBOSE: "0", "foo -c hello.foo -o hello", out: File::NULL
         | 
| 253 | 
            +
                #
         | 
| 254 | 
            +
                # after:
         | 
| 255 | 
            +
                #
         | 
| 256 | 
            +
                #   shell "foo -c hello.foo -o hello", env: { FOO_VERBOSE: "0" }, out: File::NULL
         | 
| 257 | 
            +
                #
         | 
| 258 | 
            +
                def shell(args, **options)
         | 
| 259 | 
            +
                  args = Array(args) # args is a string or array
         | 
| 260 | 
            +
                  env = options.delete(:env)
         | 
| 261 | 
            +
                  args.unshift(env) if env
         | 
| 262 | 
            +
                  sh(*args, options)
         | 
| 219 263 | 
             
                end
         | 
| 220 264 | 
             
              end
         | 
| 221 265 | 
             
            end
         | 
    
        data/lib/startup_time/options.rb
    CHANGED
    
    
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'rake'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module StartupTime
         | 
| 6 | 
            +
              # add some missing methods/aliases to Rake tasks
         | 
| 7 | 
            +
              # TODO move to a gem (or PR)
         | 
| 8 | 
            +
              module Refinements
         | 
| 9 | 
            +
                refine Rake::FileTask do
         | 
| 10 | 
            +
                  alias_method :target, :name
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                refine Rake::Task do
         | 
| 14 | 
            +
                  alias_method :append, :enhance
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def prepend(deps = nil, &block)
         | 
| 17 | 
            +
                    prerequisites.replace(Array(deps) | prerequisites) if deps
         | 
| 18 | 
            +
                    actions.unshift(block) if block_given?
         | 
| 19 | 
            +
                    self
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
    
        data/lib/startup_time/version.rb
    CHANGED
    
    
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            System.print("Hello, world!")
         | 
    
        data/resources/tests.yaml
    CHANGED
    
    | @@ -21,6 +21,7 @@ crystal: | |
| 21 21 | 
             
                - slow-compile
         | 
| 22 22 | 
             
              source: hello.cr
         | 
| 23 23 | 
             
              compile: '%{compiler} build --release -o %{target} %{source}'
         | 
| 24 | 
            +
              version: '%{compiler} --version | head -n1'
         | 
| 24 25 | 
             
              command: hello.cr.out
         | 
| 25 26 | 
             
              env:
         | 
| 26 27 | 
             
                  CRYSTAL_CACHE_DIR: .crystal
         | 
| @@ -123,7 +124,8 @@ javac: | |
| 123 124 | 
             
                - java
         | 
| 124 125 | 
             
                - jvm
         | 
| 125 126 | 
             
              source: HelloJava.java
         | 
| 126 | 
            -
               | 
| 127 | 
            +
              compile: '%{compiler} -d . %{source}'
         | 
| 128 | 
            +
              version: '%{compiler} -version' # we could dump the interpeter version as well/instead
         | 
| 127 129 | 
             
              command:
         | 
| 128 130 | 
             
                - java
         | 
| 129 131 | 
             
                - HelloJava
         | 
| @@ -313,3 +315,12 @@ truffle-ruby: | |
| 313 315 | 
             
              command:
         | 
| 314 316 | 
             
                - truffleruby
         | 
| 315 317 | 
             
                - hello.rb
         | 
| 318 | 
            +
             | 
| 319 | 
            +
            wren:
         | 
| 320 | 
            +
              name: Wren
         | 
| 321 | 
            +
              groups:
         | 
| 322 | 
            +
                - fast
         | 
| 323 | 
            +
                - script
         | 
| 324 | 
            +
              command:
         | 
| 325 | 
            +
                - wren
         | 
| 326 | 
            +
                - hello.wren
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: startup-time
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - chocolateboy
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019- | 
| 11 | 
            +
            date: 2019-09-11 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activesupport
         | 
| @@ -180,6 +180,7 @@ files: | |
| 180 180 | 
             
            - lib/startup_time/app.rb
         | 
| 181 181 | 
             
            - lib/startup_time/builder.rb
         | 
| 182 182 | 
             
            - lib/startup_time/options.rb
         | 
| 183 | 
            +
            - lib/startup_time/refinements.rb
         | 
| 183 184 | 
             
            - lib/startup_time/registry.rb
         | 
| 184 185 | 
             
            - lib/startup_time/services.rb
         | 
| 185 186 | 
             
            - lib/startup_time/util.rb
         | 
| @@ -202,6 +203,7 @@ files: | |
| 202 203 | 
             
            - resources/src/hello.py
         | 
| 203 204 | 
             
            - resources/src/hello.rb
         | 
| 204 205 | 
             
            - resources/src/hello.rs
         | 
| 206 | 
            +
            - resources/src/hello.wren
         | 
| 205 207 | 
             
            - resources/tests.yaml
         | 
| 206 208 | 
             
            homepage: https://github.com/chocolateboy/startup-time
         | 
| 207 209 | 
             
            licenses:
         | 
| @@ -226,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 226 228 | 
             
                - !ruby/object:Gem::Version
         | 
| 227 229 | 
             
                  version: '0'
         | 
| 228 230 | 
             
            requirements: []
         | 
| 229 | 
            -
            rubygems_version: 3.0. | 
| 231 | 
            +
            rubygems_version: 3.0.6
         | 
| 230 232 | 
             
            signing_key: 
         | 
| 231 233 | 
             
            specification_version: 4
         | 
| 232 234 | 
             
            summary: A command-line tool to measure the startup times of programs in various languages
         |