-
Notifications
You must be signed in to change notification settings - Fork 74
/
Copy pathdependencies.jl
1205 lines (1119 loc) · 43.3 KB
/
dependencies.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# This is the high level interface for building dependencies using the declarative BinDeps Interface
import Base: show
const OSNAME = Sys.iswindows() ? :Windows : Sys.KERNEL
if !isdefined(Base, :pairs)
pairs(x) = (a => b for (a, b) in x)
end
# A dependency provider, if successfully executed will satisfy the dependency
abstract type DependencyProvider end
# A library helper may be used by `DependencyProvider`s but will by itself not provide the library
abstract type DependencyHelper end
mutable struct PackageContext
do_install::Bool
dir::AbstractString
package::AbstractString
deps::Vector{Any}
end
mutable struct LibraryDependency
name::AbstractString
context::PackageContext
providers::Vector{Tuple{DependencyProvider,Dict{Symbol,Any}}}
helpers::Vector{Tuple{DependencyHelper,Dict{Symbol,Any}}}
properties::Dict{Symbol,Any}
libvalidate::Function
end
mutable struct LibraryGroup
name::AbstractString
deps::Vector{LibraryDependency}
end
# Default directory organization
pkgdir(dep) = dep.context.dir
depsdir(dep) = joinpath(pkgdir(dep),"deps")
usrdir(dep) = joinpath(depsdir(dep),"usr")
libdir(dep) = joinpath(usrdir(dep),"lib")
bindir(dep) = joinpath(usrdir(dep),"bin")
includedir(dep) = joinpath(usrdir(dep),"include")
builddir(dep) = joinpath(depsdir(dep),"builds")
downloadsdir(dep) = joinpath(depsdir(dep),"downloads")
srcdir(dep) = joinpath(depsdir(dep),"src")
libdir(provider, dep) = [libdir(dep), libdir(dep)*"32", libdir(dep)*"64"]
bindir(provider, dep) = bindir(dep)
successful_validate(l,p) = true
function _library_dependency(context::PackageContext, name; props...)
validate = successful_validate
group = nothing
properties = collect(pairs(props))
for i in 1:length(properties)
k,v = properties[i]
if k == :validate
validate = v
splice!(properties,i)
end
if k == :group
group = v
end
end
r = LibraryDependency(name, context, Tuple{DependencyProvider,Dict{Symbol,Any}}[], Tuple{DependencyHelper,Dict{Symbol,Any}}[], Dict{Symbol,Any}(properties), validate)
if group !== nothing
push!(group.deps,r)
else
push!(context.deps,r)
end
r
end
function _library_group(context,name)
r = LibraryGroup(name,LibraryDependency[])
push!(context.deps,r)
r
end
# This macro expects to be the first thing run. It attempts to deduce the package name and initializes the context
macro setup()
dir = normpath(joinpath(pwd(),".."))
package = basename(dir)
esc(quote
if length(ARGS) > 0 && isa(ARGS[1],BinDeps.PackageContext)
bindeps_context = ARGS[1]
else
bindeps_context = BinDeps.PackageContext(true,$dir,$package,Any[])
end
library_group(args...) = BinDeps._library_group(bindeps_context,args...)
library_dependency(args...; properties...) = BinDeps._library_dependency(bindeps_context,args...;properties...)
end)
end
export library_dependency, bindir, srcdir, usrdir, libdir
library_dependency(args...; properties...) = error("No context provided. Did you forget `@BinDeps.setup`?")
abstract type PackageManager <: DependencyProvider end
const DEBIAN_VERSION_REGEX = r"^
([0-9]+\:)? # epoch
(?:(?:([0-9][a-z0-9.\-+:~]*)-([0-9][a-z0-9.+~]*)) | # upstream version + debian revision
([0-9][a-z0-9.+:~]*)) # upstream version
"ix
const has_apt = try success(`apt-get -v`) && success(`apt-cache -v`) catch e false end
mutable struct AptGet <: PackageManager
package::AbstractString
end
can_use(::Type{AptGet}) = has_apt && Sys.islinux()
package_available(p::AptGet) = can_use(AptGet) && !isempty(available_versions(p))
function available_versions(p::AptGet)
vers = String[]
lookfor_version = false
for l in eachline(`apt-cache showpkg $(p.package)`)
if startswith(l,"Version:")
try
vs = l[(1+length("Version: ")):end]
push!(vers, vs)
catch e
# ignore error
end
elseif lookfor_version && (m = match(DEBIAN_VERSION_REGEX, l)) !== nothing
m.captures[2] !== nothing ? push!(vers, m.captures[2]) :
push!(vers, m.captures[4])
elseif startswith(l, "Versions:")
lookfor_version = true
elseif startswith(l, "Reverse Depends:")
lookfor_version = false
end
end
return vers
end
function available_version(p::AptGet)
vers = available_versions(p)
isempty(vers) && error("apt-cache did not return version information. This shouldn't happen. Please file a bug!")
length(vers) > 1 && warn("Multiple versions of $(p.package) are available. Use BinDeps.available_versions to get all versions.")
return vers[end]
end
pkg_name(a::AptGet) = a.package
libdir(p::AptGet,dep) = ["/usr/lib", "/usr/lib64", "/usr/lib32", "/usr/lib/x86_64-linux-gnu", "/usr/lib/i386-linux-gnu"]
const has_yum = try success(`yum --version`) catch e false end
mutable struct Yum <: PackageManager
package::AbstractString
end
can_use(::Type{Yum}) = has_yum && Sys.islinux()
package_available(y::Yum) = can_use(Yum) && success(`yum list $(y.package)`)
function available_version(y::Yum)
uname = readchomp(`uname -m`)
found_uname = false
found_version = false
for l in eachline(`yum info $(y.package)`)
VERSION < v"0.6" && (l = chomp(l))
if !found_uname
# On 64-bit systems, we may have multiple arches installed
# this makes sure we get the right one
found_uname = endswith(l, uname)
continue
end
if startswith(l, "Version")
return convert(VersionNumber, split(l)[end])
end
end
error("yum did not return version information. This shouldn't happen. Please file a bug!")
end
pkg_name(y::Yum) = y.package
# Pacman/Yaourt are package managers for Arch Linux.
# Note that `pacman --version` has an unreliable return value.
const has_pacman = try success(`pacman -Qq`) catch e false end
mutable struct Pacman <: PackageManager
package::AbstractString
end
can_use(::Type{Pacman}) = has_pacman && Sys.islinux()
package_available(p::Pacman) = can_use(Pacman) && success(`pacman -Si $(p.package)`)
# Only one version is usually available via pacman, hence no `available_versions`.
function available_version(p::Pacman)
for l in eachline(`pacman -Si $(p.package)`) # To circumvent alias problems
if startswith(l, "Version")
# The following isn't perfect, but it's hopefully less brittle than
# writing a regex for pacman's nonexistent version-string standard.
# This also strips away the sometimes leading epoch as in ffmpeg's
# Version : 1:2.3.3-1
versionstr = strip(split(l, ":")[end])
try
return convert(VersionNumber, versionstr)
catch e
# For too long versions like imagemagick's 6.8.9.6-1, give it
# a second try just discarding superfluous stuff.
return convert(VersionNumber, join(split(versionstr, '.')[1:3], '.'))
end
end
end
error("pacman did not return version information. This shouldn't happen. Please file a bug!")
end
pkg_name(p::Pacman) = p.package
libdir(p::Pacman,dep) = ["/usr/lib", "/usr/lib32"]
# zypper is a package manager used by openSUSE
const has_zypper = try success(`zypper --version`) catch e false end
mutable struct Zypper <: PackageManager
package::AbstractString
end
can_use(::Type{Zypper}) = has_zypper && Sys.islinux()
package_available(z::Zypper) = can_use(Zypper) && success(`zypper se $(z.package)`)
function available_version(z::Zypper)
uname = readchomp(`uname -m`)
found_uname = false
ENV2 = copy(ENV)
ENV2["LC_ALL"] = "C"
for l in eachline(setenv(`zypper info $(z.package)`, ENV2))
VERSION < v"0.6" && (l = chomp(l))
if !found_uname
found_uname = endswith(l, uname)
continue
end
if startswith(l, "Version:")
versionstr = strip(split(l, ":")[end])
return convert(VersionNumber, versionstr)
end
end
error("zypper did not return version information. This shouldn't happen. Please file a bug!")
end
pkg_name(z::Zypper) = z.package
libdir(z::Zypper,dep) = ["/usr/lib", "/usr/lib32", "/usr/lib64"]
# pkg is the system binary package manager for FreeBSD
const has_bsdpkg = try success(`pkg -v`) catch e false end
mutable struct BSDPkg <: PackageManager
package::AbstractString
end
can_use(::Type{BSDPkg}) = has_bsdpkg && Sys.KERNEL === :FreeBSD
function package_available(p::BSDPkg)
can_use(BSDPkg) || return false
rgx = Regex(string("^(", p.package, ")(\\s+.+)?\$"))
for line in eachline(`pkg search -L name $(p.package)`)
occursin(rgx, line) && return true
end
return false
end
function available_version(p::BSDPkg)
looknext = false
for line in eachline(`pkg search -L name -Q version $(p.package)`)
if rstrip(line) == p.package
looknext = true
continue
end
if looknext && startswith(line, "Version")
# Package versioning is [SOFTWARE VERSION]_[PORT REVISION],[PORT EPOCH]
# In our case we only care about the software version, not the port revision
# or epoch. The software version should be recognizable as semver-ish.
rawversion = chomp(line[findfirst(c->c==':', line)+2:end])
# Chop off the port revision and epoch by removing everything after and
# including the first underscore
libversion = replace(rawversion, r"_.+$" => "")
# This should be a valid version, but it's still possible that it isn't
if occursin(Base.VERSION_REGEX, libversion)
return VersionNumber(libversion)
else
error("\"$rawversion\" is not recognized as a version. Please report this to BinDeps.jl.")
end
end
end
error("pkg did not return version information. This should not happen. Please file a bug!")
end
pkg_name(p::BSDPkg) = p.package
libdir(p::BSDPkg, dep) = ["/usr/local/lib"]
# Can use everything else without restriction by default
can_use(::Type) = true
abstract type Sources <: DependencyHelper end
abstract type Binaries <: DependencyProvider end
#
# A dummy provider checked for every library that
# indicates the library was found somewhere on the
# system using dlopen.
#
struct SystemPaths <: DependencyProvider; end
show(io::IO, ::SystemPaths) = print(io,"System Paths")
using URIParser
export URI
mutable struct NetworkSource <: Sources
uri::URI
end
srcdir(s::Sources, dep::LibraryDependency) = srcdir(dep,s,Dict{Symbol,Any}())
function srcdir( dep::LibraryDependency, s::NetworkSource,opts)
joinpath(srcdir(dep),get(opts,:unpacked_dir,splittarpath(basename(s.uri.path))[1]))
end
mutable struct RemoteBinaries <: Binaries
uri::URI
end
mutable struct CustomPathBinaries <: Binaries
path::AbstractString
end
libdir(p::CustomPathBinaries,dep) = p.path
abstract type BuildProcess <: DependencyProvider end
mutable struct SimpleBuild <: BuildProcess
steps
end
mutable struct Autotools <: BuildProcess
source
opts
end
mutable struct GetSources <: BuildStep
dep::LibraryDependency
end
lower(x::GetSources,collection) = push!(collection,generate_steps(x.dep,gethelper(x.dep,Sources)...))
Autotools(;opts...) = Autotools(nothing, Dict{Any,Any}(pairs(opts)))
export AptGet, Yum, Pacman, Zypper, BSDPkg, Sources, Binaries, provides, BuildProcess, Autotools,
GetSources, SimpleBuild, available_version
provider(::Type{T},package::AbstractString; opts...) where {T <: PackageManager} = T(package)
provider(::Type{Sources},uri::URI; opts...) = NetworkSource(uri)
provider(::Type{Binaries},uri::URI; opts...) = RemoteBinaries(uri)
provider(::Type{Binaries},path::AbstractString; opts...) = CustomPathBinaries(path)
provider(::Type{SimpleBuild},steps; opts...) = SimpleBuild(steps)
provider(::Type{BuildProcess},p::T; opts...) where {T <: BuildProcess} = provider(T,p; opts...)
provider(::Type{BuildProcess},steps::Union{BuildStep,SynchronousStepCollection}; opts...) = provider(SimpleBuild,steps; opts...)
provider(::Type{Autotools},a::Autotools; opts...) = a
provides(provider::DependencyProvider,dep::LibraryDependency; opts...) = push!(dep.providers,(provider,Dict{Symbol,Any}(pairs(opts))))
provides(helper::DependencyHelper,dep::LibraryDependency; opts...) = push!(dep.helpers,(helper,Dict{Symbol,Any}(pairs(opts))))
provides(::Type{T},p,dep::LibraryDependency; opts...) where {T} = provides(provider(T,p; opts...),dep; opts...)
function provides(::Type{T},packages::AbstractArray,dep::LibraryDependency; opts...) where {T}
for p in packages
provides(T,p,dep; opts...)
end
end
function provides(::Type{T},ps,deps::Vector{LibraryDependency}; opts...) where {T}
p = provider(T,ps; opts...)
for dep in deps
provides(p,dep; opts...)
end
end
function provides(::Type{T},providers::Dict; opts...) where {T}
for (k,v) in providers
provides(T,k,v;opts...)
end
end
sudoname(c::Cmd) = c == `` ? "" : "sudo "
const have_sonames = Ref(false)
const sonames = Dict{String,String}()
function reread_sonames()
if VERSION >= v"0.7.0-DEV.1287" # only use this where julia issue #22832 is fixed
empty!(sonames)
have_sonames[] = false
nothing
else
ccall(:jl_read_sonames, Cvoid, ())
end
end
if Sys.iswindows() || Sys.isapple()
function read_sonames()
have_sonames[] = true
end
elseif Sys.islinux()
let ldconfig_arch = Dict(:i386 => "x32",
:i387 => "x32",
:i486 => "x32",
:i586 => "x32",
:i686 => "x32",
:x86_64 => "x86-64",
:aarch64 => "AArch64"),
arch = get(ldconfig_arch, Sys.ARCH, ""),
arch_wrong = filter!(x -> (x != arch), ["x32", "x86-64", "AArch64", "soft-float"])
global read_sonames
function read_sonames()
empty!(sonames)
# Some Linux distros do not expose executables from /sbin and /usr/sbin via PATH,
# so we append these here explicitly
ldconfig_path = ENV["PATH"] * ":/usr/local/sbin:/usr/sbin:/sbin"
lines_ldconfig = withenv("PATH" => ldconfig_path) do
eachline(`ldconfig -p`)
end
for line in lines_ldconfig
VERSION < v"0.6" && (line = chomp(line))
m = match(r"^\s+([^ ]+)\.so[^ ]* \(([^)]*)\) => (.+)$", line)
if m !== nothing
desc = m[2]
if Sys.WORD_SIZE != 32 && !isempty(arch)
occursin(arch, desc) || continue
end
for wrong in arch_wrong
occursin(wrong, desc) && continue
end
sonames[m[1]] = m[3]
end
end
have_sonames[] = true
end
end
else
function read_sonames()
empty!(sonames)
for line in eachline(`ldconfig -r`)
m = match(r"^\s+\d+:-l([^ ]+)\.[^. ]+ => (.+)$", line)
if m !== nothing
sonames["lib" * m[1]] = m[2]
end
end
have_sonames[] = true
end
end
if VERSION >= v"0.7.0-DEV.1287" # only use this where julia issue #22832 is fixed
lookup_soname(s) = lookup_soname(String(s))
function lookup_soname(s::String)
have_sonames[] || read_sonames()
return get(sonames, s, "")
end
else
function lookup_soname(lib)
if Sys.islinux() || (Sys.isbsd() && !Sys.isapple())
soname = ccall(:jl_lookup_soname, Ptr{UInt8}, (Ptr{UInt8}, Csize_t), lib, sizeof(lib))
soname != C_NULL && return unsafe_string(soname)
end
return ""
end
end
generate_steps(h::DependencyProvider,dep::LibraryDependency) = error("Must also pass provider options")
generate_steps(h::BuildProcess,dep::LibraryDependency,opts) = h.steps
function generate_steps(dep::LibraryDependency,h::AptGet,opts)
if get(opts,:force_rebuild,false)
error("Will not force apt-get to rebuild dependency \"$(dep.name)\".\n"*
"Please make any necessary adjustments manually (This might just be a version upgrade)")
end
sudo = get(opts, :sudo, has_sudo[]) ? `sudo` : ``
@build_steps begin
println("Installing dependency $(h.package) via `$(sudoname(sudo))apt-get install $(h.package)`:")
`$sudo apt-get install $(h.package)`
reread_sonames
end
end
function generate_steps(dep::LibraryDependency,h::Yum,opts)
if get(opts,:force_rebuild,false)
error("Will not force yum to rebuild dependency \"$(dep.name)\".\n"*
"Please make any necessary adjustments manually (This might just be a version upgrade)")
end
sudo = get(opts, :sudo, has_sudo[]) ? `sudo` : ``
@build_steps begin
println("Installing dependency $(h.package) via `$(sudoname(sudo))yum install $(h.package)`:")
`$sudo yum install $(h.package)`
reread_sonames
end
end
function generate_steps(dep::LibraryDependency,h::Pacman,opts)
if get(opts,:force_rebuild,false)
error("Will not force pacman to rebuild dependency \"$(dep.name)\".\n"*
"Please make any necessary adjustments manually (This might just be a version upgrade)")
end
sudo = get(opts, :sudo, has_sudo[]) ? `sudo` : ``
@build_steps begin
println("Installing dependency $(h.package) via `$(sudoname(sudo))pacman -S --needed $(h.package)`:")
`$sudo pacman -S --needed $(h.package)`
reread_sonames
end
end
function generate_steps(dep::LibraryDependency,h::Zypper,opts)
if get(opts,:force_rebuild,false)
error("Will not force zypper to rebuild dependency \"$(dep.name)\".\n"*
"Please make any necessary adjustments manually (This might just be a version upgrade)")
end
sudo = get(opts, :sudo, has_sudo[]) ? `sudo` : ``
@build_steps begin
println("Installing dependency $(h.package) via `$(sudoname(sudo))zypper install $(h.package)`:")
`$sudo zypper install $(h.package)`
reread_sonames
end
end
function generate_steps(dep::LibraryDependency, p::BSDPkg, opts)
if get(opts, :force_rebuild, false)
error("Will not force pkg to rebuild dependency \"$(dep.name)\".\n" *
"Please make any necessary adjustments manually. (This might just be a version upgrade.)")
end
sudo = get(opts, :sudo, has_sudo[]) ? `sudo` : ``
@build_steps begin
println("Installing dependency $(p.package) via `$(sudoname(sudo))pkg install -y $(p.package)`:`")
`$sudo pkg install -y $(p.package)`
reread_sonames
end
end
function generate_steps(dep::LibraryDependency,h::NetworkSource,opts)
localfile = joinpath(downloadsdir(dep),get(opts,:filename,basename(h.uri.path)))
@build_steps begin
FileDownloader(string(h.uri),localfile)
ChecksumValidator(get(opts,:SHA,get(opts,:sha,"")),localfile)
CreateDirectory(srcdir(dep))
FileUnpacker(localfile,srcdir(dep),srcdir(dep,h,opts))
end
end
function generate_steps(dep::LibraryDependency,h::RemoteBinaries,opts)
get(opts,:force_rebuild,false) && error("Force rebuild not allowed for binaries. Use a different download location instead.")
localfile = joinpath(downloadsdir(dep),get(opts,:filename,basename(h.uri.path)))
# choose the destination to unpack into and the folder/file to validate
(dest, target) = if haskey(opts, :unpacked_dir)
if opts[:unpacked_dir] == "."
# if the archive dumps right in the root dir, create a subdir
(joinpath(depsdir(dep), dep.name), ".")
else
(depsdir(dep), opts[:unpacked_dir])
end
else
(depsdir(dep), "usr")
end
steps = @build_steps begin
FileDownloader(string(h.uri),localfile)
ChecksumValidator(get(opts,:SHA,get(opts,:sha,"")),localfile)
FileUnpacker(localfile,dest,target)
end
end
generate_steps(dep::LibraryDependency,h::SimpleBuild,opts) = h.steps
function getoneprovider(dep::LibraryDependency,method)
for (p,opts) = dep.providers
if typeof(p) <: method && can_use(typeof(p))
return (p,opts)
end
end
return (nothing,nothing)
end
function getallproviders(dep::LibraryDependency,method)
ret = Any[]
for (p,opts) = dep.providers
if typeof(p) <: method && can_use(typeof(p))
push!(ret,(p,opts))
end
end
ret
end
function gethelper(dep::LibraryDependency,method)
for (p,opts) = dep.helpers
if typeof(p) <: method
return (p,opts)
end
end
return (nothing,nothing)
end
# convert aliases="foo" into aliases=["foo"] to avoid iterating over characters 'f' 'o' 'o'
stringarray(s::AbstractString) = [s]
stringarray(s) = s
function generate_steps(dep::LibraryDependency,method)
(p,opts) = getoneprovider(dep,method)
p !== nothing && return generate_steps(p,dep,opts)
(p,hopts) = gethelper(dep,method)
p !== nothing && return generate_steps(p,dep,hopts)
error("No provider or helper for method $method found for dependency $(dep.name)")
end
function generate_steps(dep::LibraryDependency, h::Autotools, provider_opts)
if h.source === nothing
h.source = gethelper(dep,Sources)
end
if isa(h.source,Sources)
h.source = (h.source,Dict{Symbol,Any}())
end
h.source[1] === nothing && error("Could not obtain sources for dependency $(dep.name)")
steps = lower(generate_steps(dep,h.source...))
opts = Dict()
opts[:srcdir] = srcdir(dep,h.source...)
opts[:prefix] = usrdir(dep)
opts[:builddir] = joinpath(builddir(dep),dep.name)
merge!(opts,h.opts)
if haskey(opts,:installed_libname)
!haskey(opts,:installed_libpath) || error("Can't specify both installed_libpath and installed_libname")
opts[:installed_libpath] = String[joinpath(libdir(dep),opts[:installed_libname])]
delete!(opts, :installed_libname)
elseif !haskey(opts,:installed_libpath)
opts[:installed_libpath] = String[joinpath(libdir(dep),x)*"."*Libdl.dlext for x in stringarray(get(dep.properties,:aliases,String[]))]
end
if !haskey(opts,:libtarget) && haskey(dep.properties,:aliases)
opts[:libtarget] = String[x*"."*Libdl.dlext for x in stringarray(dep.properties[:aliases])]
end
if !haskey(opts,:include_dirs)
opts[:include_dirs] = AbstractString[]
end
if !haskey(opts,:lib_dirs)
opts[:lib_dirs] = AbstractString[]
end
if !haskey(opts,:pkg_config_dirs)
opts[:pkg_config_dirs] = AbstractString[]
end
if !haskey(opts,:rpath_dirs)
opts[:rpath_dirs] = AbstractString[]
end
if haskey(opts,:configure_subdir)
opts[:srcdir] = joinpath(opts[:srcdir],opts[:configure_subdir])
delete!(opts, :configure_subdir)
end
pushfirst!(opts[:include_dirs],includedir(dep))
pushfirst!(opts[:lib_dirs],libdir(dep))
pushfirst!(opts[:rpath_dirs],libdir(dep))
pushfirst!(opts[:pkg_config_dirs],joinpath(libdir(dep),"pkgconfig"))
env = Dict{String,String}()
env["PKG_CONFIG_PATH"] = join(opts[:pkg_config_dirs],":")
delete!(opts,:pkg_config_dirs)
if Sys.isunix()
env["PATH"] = bindir(dep)*":"*ENV["PATH"]
elseif Sys.iswindows()
env["PATH"] = bindir(dep)*";"*ENV["PATH"]
end
haskey(opts,:env) && merge!(env,opts[:env])
opts[:env] = env
if get(provider_opts,:force_rebuild,false)
opts[:force_rebuild] = true
end
steps |= AutotoolsDependency(;opts...)
steps
end
const EXTENSIONS = ["", "." * Libdl.dlext]
# Finds all copies of the library on the system, listed in preference order.
# Return value is an array of tuples of the provider and the path where it is found
function _find_library(dep::LibraryDependency; provider = Any)
ret = Any[]
# Same as find_library, but with extra check defined by dep
libnames = [dep.name;get(dep.properties,:aliases,String[])]
# Make sure we keep the defaults first, but also look in the other directories
providers = unique([reduce(vcat,[getallproviders(dep,p) for p in defaults]);dep.providers])
for (p,opts) in providers
(p !== nothing && can_use(typeof(p)) && can_provide(p,opts,dep)) || continue
paths = AbstractString[]
# Allow user to override installation path
if haskey(opts,:installed_libpath) && isdir(opts[:installed_libpath])
pushfirst!(paths,opts[:installed_libpath])
end
ppaths = libdir(p,dep)
append!(paths,isa(ppaths,Array) ? ppaths : [ppaths])
if haskey(opts,:unpacked_dir)
dir = opts[:unpacked_dir]
if dir == "." && isdir(joinpath(depsdir(dep), dep.name))
# the archive unpacks into the root, so we created a subdir with the dep name
push!(paths, joinpath(depsdir(dep), dep.name))
elseif isdir(joinpath(depsdir(dep),dir))
push!(paths,joinpath(depsdir(dep),dir))
end
end
# Windows, do you know what `lib` stands for???
if Sys.iswindows()
push!(paths,bindir(p,dep))
end
(isempty(paths) || all(map(isempty,paths))) && continue
for lib in libnames, path in paths
l = joinpath(path, lib)
h = Libdl.dlopen_e(l, Libdl.RTLD_LAZY)
if h != C_NULL
works = dep.libvalidate(l,h)
l = Libdl.dlpath(h)
Libdl.dlclose(h)
if works
push!(ret, ((p, opts), l))
else
# We tried to load this providers' library, but it didn't satisfy
# the requirements, so tell it to force a rebuild since the requirements
# have most likely changed
opts[:force_rebuild] = true
end
end
end
end
# Now check system libraries
for lib in libnames
# We don't want to use regular dlopen, because we want to get at
# system libraries even if one of our providers is higher in the
# DL_LOAD_PATH
for path in Libdl.DL_LOAD_PATH
for ext in EXTENSIONS
opath = string(joinpath(path,lib),ext)
check_path!(ret,dep,opath)
end
end
for ext in EXTENSIONS
opath = string(lib,ext)
check_path!(ret,dep,opath)
end
soname = lookup_soname(lib)
isempty(soname) || check_path!(ret, dep, soname)
end
return ret
end
function check_path!(ret, dep, opath)
flags = Libdl.RTLD_LAZY
handle = ccall(:jl_dlopen, Ptr{Cvoid}, (Cstring, Cuint), opath, flags)
try
check_system_handle!(ret, dep, handle)
finally
handle != C_NULL && Libdl.dlclose(handle)
end
end
function check_system_handle!(ret,dep,handle)
if handle != C_NULL
libpath = Libdl.dlpath(handle)
# Check that this is not a duplicate
for p in ret
try
if realpath(p[2]) == realpath(libpath)
return
end
catch
warn("""
Found a library that does not exist.
This may happen if the library has an active open handle.
Please quit julia and try again.
""")
return
end
end
works = dep.libvalidate(libpath,handle)
if works
push!(ret, ((SystemPaths(),Dict()), libpath))
end
end
end
# Default installation method
defaults = if Sys.isapple()
[Binaries, PackageManager, SystemPaths, BuildProcess]
elseif Sys.isbsd() || (Sys.islinux() && glibc_version() === nothing) # non-glibc
[PackageManager, SystemPaths, BuildProcess]
elseif Sys.islinux() # glibc
[PackageManager, SystemPaths, Binaries, BuildProcess]
elseif Sys.iswindows()
[Binaries, PackageManager, SystemPaths]
else
[SystemPaths, BuildProcess]
end
function applicable(dep::LibraryDependency)
if haskey(dep.properties,:os)
if (dep.properties[:os] != OSNAME && dep.properties[:os] != :Unix) || (dep.properties[:os] == :Unix && !Sys.isunix())
return false
end
elseif haskey(dep.properties,:runtime) && dep.properties[:runtime] == false
return false
end
return true
end
applicable(deps::LibraryGroup) = any([applicable(dep) for dep in deps.deps])
function can_provide(p,opts,dep)
if p === nothing || (haskey(opts,:os) && opts[:os] != OSNAME && (opts[:os] != :Unix || !Sys.isunix()))
return false
end
if !haskey(opts,:validate)
return true
elseif isa(opts[:validate],Bool)
return opts[:validate]
else
return opts[:validate](p,dep)
end
end
function can_provide(p::PackageManager,opts,dep)
if p === nothing || (haskey(opts,:os) && opts[:os] != OSNAME && (opts[:os] != :Unix || !Sys.isunix()))
return false
end
if !package_available(p)
return false
end
if !haskey(opts,:validate)
return true
elseif isa(opts[:validate],Bool)
return opts[:validate]
else
return opts[:validate](p,dep)
end
end
issatisfied(dep::LibraryDependency) = !isempty(_find_library(dep))
allf(deps) = Dict([(dep, _find_library(dep)) for dep in deps.deps])
function satisfied_providers(deps::LibraryGroup, allfl = allf(deps))
viable_providers = nothing
for dep in deps.deps
if !applicable(dep)
continue
end
providers = map(x->typeof(x[1][1]),allfl[dep])
if viable_providers == nothing
viable_providers = providers
else
viable_providers = intersect(viable_providers,providers)
end
end
viable_providers
end
function viable_providers(deps::LibraryGroup)
vp = nothing
for dep in deps.deps
if !applicable(dep)
continue
end
providers = map(x->typeof(x[1]),dep.providers)
if vp === nothing
vp = providers
else
vp = intersect(vp,providers)
end
end
vp
end
#
# We need to make sure all libraries are satisfied with the
# additional constraint that all of them are satisfied by the
# same provider.
#
issatisfied(deps::LibraryGroup) = !isempty(satisfied_providers(deps))
function _find_library(deps::LibraryGroup, allfl = allf(deps); provider = Any)
providers = satisfied_providers(deps,allfl)
p = nothing
if isempty(providers)
return Dict()
else
for p2 in providers
if p2 <: provider
p = p2
end
end
end
p === nothing && error("Given provider does not satisfy the library group")
Dict([(dep, begin
thisfl = allfl[dep]
ret = nothing
for fl in thisfl
if isa(fl[1][1],p)
ret = fl
break
end
end
@assert ret != nothing
ret
end) for dep in filter(applicable,deps.deps)])
end
function satisfy!(deps::LibraryGroup, methods = defaults)
sp = satisfied_providers(deps)
if !isempty(sp)
for m in methods
for s in sp
if s <: m
return s
end
end
end
end
if !applicable(deps)
return Any
end
vp = viable_providers(deps)
didsatisfy = false
for method in methods
for p in vp
if !(p <: method) || !can_use(p)
continue
end
skip = false
for dep in deps.deps
!applicable(dep) && continue
hasany = false
for (p2,opts) in getallproviders(dep,p)
can_provide(p2, opts, dep) && (hasany = true)
end
if !hasany
skip = true
break
end
end
if skip
continue
end
for dep in deps.deps
satisfy!(dep,[p])
end
return p
end
end
error("""
None of the selected providers could satisfy library group $(deps.name)
Use BinDeps.debug(package_name) to see available providers
""")
end
function satisfy!(dep::LibraryDependency, methods = defaults)
sp = map(x->typeof(x[1][1]),_find_library(dep))
if !isempty(sp)
for m in methods
for s in sp
if s <: m
return s
end
end
end
end
if !applicable(dep)
return
end
for method in methods
for (p,opts) in getallproviders(dep,method)
can_provide(p,opts,dep) || continue
if haskey(opts,:force_depends)
for (dmethod,ddep) in opts[:force_depends]
(dp,dopts) = getallproviders(ddep,dmethod)[1]
run(lower(generate_steps(ddep,dp,dopts)))
end
end
run(lower(generate_steps(dep,p,opts)))
!issatisfied(dep) && error("Provider $method failed to satisfy dependency $(dep.name). Maybe you forgot to declare an alias in a library_dependency?")
return p
end
end
error("""
None of the selected providers can install dependency $(dep.name).
Use BinDeps.debug(package_name) to see available providers
""")
end
execute(dep::LibraryDependency,method) = run(lower(generate_steps(dep,method)))
macro install(_libmaps...)
if length(_libmaps) == 0
return esc(quote
if bindeps_context.do_install
for d in bindeps_context.deps
BinDeps.satisfy!(d)
end
end
end)
else
libmaps = eval(_libmaps[1])
load_cache = gensym()
ret = Expr(:block)
push!(ret.args,
esc(quote
load_cache = Dict()
pre_hooks = Set{$AbstractString}()
load_hooks = Set{$AbstractString}()
if bindeps_context.do_install
for d in bindeps_context.deps
p = BinDeps.satisfy!(d)
libs = BinDeps._find_library(d; provider = p)
if isa(d, BinDeps.LibraryGroup)
if !isempty(libs)
for dep in d.deps
!BinDeps.applicable(dep) && continue
if !haskey(load_cache, dep.name)
load_cache[dep.name] = libs[dep][2]
opts = libs[dep][1][2]
haskey(opts, :preload) && push!(pre_hooks,opts[:preload])
haskey(opts, :onload) && push!(load_hooks,opts[:onload])
end
end
end
else
for (k,v) in libs
if !haskey(load_cache, d.name)