-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Utilities for SnoopCompile Github actions (#49)
- Loading branch information
Showing
11 changed files
with
648 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
# SnoopCompile Bot (EXPERIMENTAL) | ||
|
||
You can use SnoopCompile bot to automatically and continuously create precompile files. | ||
|
||
One should add 3 things to a package to make the bot work: | ||
|
||
---------------------------------- | ||
|
||
|
||
- Workflow file: | ||
|
||
create a workflow file with this path in your repository `.github/workflows/SnoopCompile.yml` and use the following content: | ||
|
||
```yaml | ||
name: SnoopCompile | ||
|
||
on: | ||
- push | ||
|
||
|
||
jobs: | ||
build: | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
julia-version: ['nightly'] | ||
julia-arch: [x64] | ||
os: [ubuntu-latest] | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: julia-actions/setup-julia@latest | ||
with: | ||
version: ${{ matrix.julia-version }} | ||
- name: Install dependencies | ||
run: julia --project -e 'using Pkg; Pkg.instantiate();' | ||
- name : Add SnoopCompile and current package | ||
run: julia -e 'using Pkg; Pkg.add("SnoopCompile"); Pkg.develop(PackageSpec(; path=pwd()));' | ||
- name: Install Test dependencies | ||
run: julia -e 'using SnoopCompile; SnoopCompile.addtestdep()' | ||
- name: Generating precompile files | ||
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopCompile.jl")' | ||
- name: Running Benchmark | ||
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopBenchmark.jl")' | ||
|
||
# https://github.com/marketplace/actions/create-pull-request | ||
- name: Create Pull Request | ||
uses: peter-evans/create-pull-request@v2.1.0 | ||
with: | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
commit-message: Update precompile_*.jl file | ||
committer: YOUR NAME <yourEmail@something.com> # Change `committer` to your name and your email. | ||
title: '[AUTO] Update precompile_*.jl file' | ||
labels: SnoopCompile | ||
branch: create-pull-request/SnoopCompile | ||
- name: Check output environment variable | ||
run: echo "Pull Request Number - ${{ env.PULL_REQUEST_NUMBER }}" | ||
``` | ||
`Install Test dependencies` step is only needed if you have test dependencies other than Test. Otherwise, you should comment it. In this case, if your examples or tests have dependencies, you should add a `Test.toml` to your test folder. | ||
|
||
```yaml | ||
- name: Install Test dependencies | ||
run: julia -e 'using SnoopCompile; SnoopCompile.addtestdep()' | ||
``` | ||
|
||
For example for MatLang package: | ||
|
||
[Link](https://github.com/juliamatlab/MatLang/blob/master/.github/workflows/SnoopCompile.yml) | ||
|
||
---------------------------------- | ||
|
||
|
||
- Precompile script | ||
|
||
Add a `snoopCompile.jl` file under `deps/SnoopCompile`. The content of the file should be a script that "exercises" the functionality you'd like to precompile. One option is to use your package's `"runtests.jl"` file, or you can write a custom script for this purpose. | ||
|
||
|
||
For example, some examples that call the functions: | ||
|
||
```julia | ||
using SnoopCompile | ||
@snoopiBot "MatLang" begin | ||
using MatLang | ||
examplePath = joinpath(dirname(dirname(pathof(MatLang))), "examples") | ||
include(joinpath(examplePath,"Language_Fundamentals", "usage_Entering_Commands.jl")) | ||
include(joinpath(examplePath,"Language_Fundamentals", "usage_Matrices_and_Arrays.jl")) | ||
include(joinpath(examplePath,"Language_Fundamentals", "Data_Types", "usage_Numeric_Types.jl")) | ||
end | ||
``` | ||
[Ref]( https://github.com/juliamatlab/MatLang/blob/master/deps/SnoopCompile/snoopCompile.jl) | ||
|
||
or if you do not have additional examples, you can use your runtests.jl file using this syntax: | ||
|
||
```julia | ||
using SnoopCompile | ||
# using runtests: | ||
@snoopiBot "MatLang" | ||
``` | ||
|
||
[Also look at this](https://timholy.github.io/SnoopCompile.jl/stable/snoopi/#Precompile-scripts-1) | ||
|
||
---------------------------------- | ||
|
||
- Include precompile signatures | ||
|
||
Two lines of (commented) code that includes the precompile file in your main module. | ||
|
||
It is better to have these lines commented to continuously develop and change your package offline. snoopiBot will find these lines of code and will uncomment them in the created pull request. If they are not commented the bot will leave it as is in the pull request: | ||
|
||
```julia | ||
# include("../deps/SnoopCompile/precompile/precompile_MatLang.jl") | ||
# _precompile_() | ||
``` | ||
|
||
[Ref](https://github.com/juliamatlab/MatLang/blob/072ff8ed9877cbb34f8583ae2cf928a5df18aa0c/src/MatLang.jl#L26) | ||
|
||
|
||
---------------------------------- | ||
|
||
|
||
## Benchmark | ||
|
||
To measure the effect of adding precompile files. Add a `snoopBenchmark.jl`. The content of this file can be the following: | ||
|
||
Benchmarking the load infer time | ||
```julia | ||
println("loading infer benchmark") | ||
@snoopiBench "MatLang" using MatLang | ||
``` | ||
|
||
Benchmarking the example infer time | ||
```julia | ||
println("examples infer benchmark") | ||
@snoopiBench "MatLang" begin | ||
using MatLang | ||
examplePath = joinpath(dirname(dirname(pathof(MatLang))), "examples") | ||
# include(joinpath(examplePath,"Language_Fundamentals", "usage_Entering_Commands.jl")) | ||
include(joinpath(examplePath,"Language_Fundamentals", "usage_Matrices_and_Arrays.jl")) | ||
include(joinpath(examplePath,"Language_Fundamentals", "Data_Types", "usage_Numeric_Types.jl")) | ||
end | ||
``` | ||
|
||
Benchmarking the tests: | ||
```julia | ||
@snoopiBench "MatLang" | ||
``` | ||
[Ref](https://github.com/juliamatlab/MatLang/blob/master/deps/SnoopCompile/snoopBenchmark.jl) | ||
|
||
|
||
To run the benchmark online, add the following to your yaml file after `Generating precompile files` step: | ||
|
||
```yaml | ||
- name: Running Benchmark | ||
run: julia --project=@. -e 'include("deps/SnoopCompile/snoopBenchmark.jl")' | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
export precompile_activator, precompile_deactivator, precompile_pather, @snoopiBot, @snoopiBench, BotConfig | ||
|
||
const UStrings = Union{AbstractString,Regex,AbstractChar} | ||
################################################################ | ||
""" | ||
BotConfig | ||
Config object that holds the options and configuration for the SnoopCompile bot. This object is fed to the `@snoopiBot`. | ||
# Arguments: | ||
- `packageName::String` | ||
- `subst::Vector{Pair{UStrings, UStrings}}` : to replace a packages precompile setences with another's package like `["ImageTest" => "Images"]` | ||
- `blacklist::Vector{UStrings}` : to remove some precompile sentences | ||
`const UStrings == Union{AbstractString,Regex,AbstractChar}` # every string like type that `replace()` has a method for. | ||
""" | ||
struct BotConfig | ||
packageName::String | ||
subst::Vector{Pair{T1, T2}} where {T1<:UStrings, T2 <: UStrings} | ||
blacklist::Vector{T3} where {T3<:UStrings} | ||
end | ||
|
||
function BotConfig(packageName::String; subst::Vector{Pair{T1, T2}} where {T1<:UStrings, T2 <: UStrings} = Vector{Pair{String, String}}(), blacklist::Vector{T3} where {T3<:UStrings}= String[]) | ||
return BotConfig(packageName, subst, blacklist) | ||
end | ||
|
||
include("bot/botutils.jl") | ||
include("bot/precompileInclude.jl") | ||
include("bot/snoopiBot.jl") | ||
include("bot/snoopiBench.jl") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
################################################################ | ||
import Pkg | ||
""" | ||
Should be removed once Pkg allows adding test dependencies to the current environment | ||
Used in Github Action workflow yaml file | ||
""" | ||
function addtestdep() | ||
if isfile("test/Test.toml") | ||
testToml = Pkg.Types.parse_toml("test/Test.toml") | ||
else | ||
error("please add a Test.toml to the /test directory for test dependencies") | ||
end | ||
|
||
for (name, uuid) in testToml["deps"] | ||
Pkg.add(Pkg.PackageSpec(name = name, uuid = uuid)) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
|
||
""" | ||
precompile_pather(packageName::String) | ||
To get the path of precompile_packageName.jl file | ||
Written exclusively for SnoopCompile Github actions. | ||
# Examples | ||
```julia | ||
precompilePath, precompileFolder = precompile_pather("MatLang") | ||
``` | ||
""" | ||
function precompile_pather(packageName::String) | ||
return "\"../deps/SnoopCompile/precompile/precompile_$packageName.jl\"", | ||
"$(pwd())/deps/SnoopCompile/precompile/" | ||
end | ||
|
||
precompile_pather(packageName::Symbol) = precompile_pather(string(packageName)) | ||
precompile_pather(packageName::Module) = precompile_pather(string(packageName)) | ||
|
||
################################################################ | ||
|
||
function precompile_regex(precompilePath) | ||
# https://stackoverflow.com/questions/3469080/match-whitespace-but-not-newlines | ||
# {1,} for any number of spaces | ||
c1 = Regex("#[^\\S\\r\\n]{0,}include\\($(precompilePath)\\)") | ||
c2 = r"#\s{0,}_precompile_\(\)" | ||
a1 = "include($precompilePath)" | ||
a2 = "_precompile_()" | ||
return c1, c2, a1, a2 | ||
end | ||
################################################################ | ||
|
||
""" | ||
precompile_activator(packagePath, precompilePath) | ||
Activates precompile of a package by adding or uncommenting include() of *.jl file generated by SnoopCompile and _precompile_(). | ||
packagePath is the same as `pathof`. However, `pathof(module)` isn't used to prevent loadnig the package. | ||
Written exclusively for SnoopCompile Github actions. | ||
""" | ||
function precompile_activator(packagePath::String, precompilePath::String) | ||
|
||
packageText = Base.read(packagePath, String) | ||
|
||
c1, c2, a1, a2 = precompile_regex(precompilePath) | ||
|
||
# Checking availability of _precompile_ code | ||
commented = occursin(c1, packageText) && occursin(c2, packageText) | ||
available = occursin(a1, packageText) && occursin(a2, packageText) | ||
|
||
if commented | ||
packageEdited = foldl(replace, | ||
( | ||
c1 => a1, | ||
c2 => a2, | ||
), | ||
init = packageText) | ||
|
||
Base.write(packagePath, packageEdited) | ||
|
||
println("precompile is activated") | ||
elseif available | ||
# do nothing | ||
println("precompile is already activated") | ||
else | ||
# TODO: add code automatiaclly | ||
error(""" add the following codes into your PackageName.jl file under src folder: | ||
#include($precompilePath) | ||
#_precompile_() | ||
""") | ||
end | ||
|
||
end | ||
|
||
""" | ||
precompile_deactivator(packagePath, precompilePath) | ||
Deactivates precompile of a package by commenting include() of *.jl file generated by SnoopCompile and _precompile_(). | ||
packagePath is the same as `pathof`. However, `pathof(module)` isn't used to prevent loadnig the package. | ||
Written exclusively for SnoopCompile Github actions. | ||
""" | ||
function precompile_deactivator(packagePath::String, precompilePath::String) | ||
|
||
packageText = Base.read(packagePath, String) | ||
|
||
c1, c2, a1, a2 = precompile_regex(precompilePath) | ||
|
||
# Checking availability of _precompile_ code | ||
commented = occursin(c1, packageText) && occursin(c2, packageText) | ||
available = occursin(a1, packageText) && occursin(a2, packageText) | ||
|
||
if available && !commented | ||
packageEdited = foldl(replace, | ||
( | ||
a1 => "#"*a1, | ||
a2 => "#"*a2, | ||
), | ||
init = packageText) | ||
|
||
Base.write(packagePath, packageEdited) | ||
|
||
println("precompile is deactivated") | ||
elseif commented | ||
# do nothing | ||
println("precompile is already deactivated") | ||
else | ||
# TODO: add code automatiaclly | ||
error(""" add the following codes into your PackageName.jl file under src folder: | ||
#include($precompilePath) | ||
#_precompile_() | ||
""") | ||
end | ||
|
||
end |
Oops, something went wrong.