caffe 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +110 -0
- data/ext/caffe/blob.cc +145 -0
- data/ext/caffe/blob.hpp +32 -0
- data/ext/caffe/caffe.cc +13 -0
- data/ext/caffe/common.cc +36 -0
- data/ext/caffe/common.hpp +17 -0
- data/ext/caffe/extconf.rb +39 -0
- data/ext/caffe/mkmf_cxx.rb +158 -0
- data/ext/caffe/net.cc +55 -0
- data/ext/caffe/net.hpp +10 -0
- data/ext/caffe/util.hpp +39 -0
- data/lib/caffe.rb +5 -0
- data/lib/caffe/blob.rb +35 -0
- data/lib/caffe/caffe.pb.rb +902 -0
- data/lib/caffe/common.rb +6 -0
- data/lib/caffe/version.rb +3 -0
- data/spec/blob_spec.rb +104 -0
- data/spec/common_spec.rb +26 -0
- data/spec/net/gen_data.rb +29 -0
- data/spec/net/test.caffemodel +0 -0
- data/spec/net/test.prototxt +71 -0
- data/spec/net/test_solver.prototxt +14 -0
- data/spec/net/test_train.prototxt +83 -0
- data/spec/net_spec.rb +71 -0
- data/spec/spec_helper.rb +16 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 87a24cb6239268eff1a716f0f770edf6b708d0f3
|
4
|
+
data.tar.gz: 4e589a9b416d7191567d6d07bc7b127d6bded9d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 768510fe5634d31bd847593679836ee2b123264693ce3698dd421c30129fa76b7c26194939669e4e55c904c5def2825b5941a9cafec8df0cc2f756a9d146ac15
|
7
|
+
data.tar.gz: b3a6c3de6efe2190be72ac7637c81f300182b7c5b4417fedbfff28eaf42a5f02eab71dc00c17aadf752f104ad60ec1a6110f983d1c78f786a1b1e8f9629da2cb
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Tiny Tiny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Ruby Caffe #
|
2
|
+
|
3
|
+
A ruby wrapper for the deep learning framework
|
4
|
+
|
5
|
+
## Linking caffe ##
|
6
|
+
|
7
|
+
There is a linking problem since `caffe` doesn't provide an official installation plan, but everything in its build path / distribute path instead of somewhere like `/usr` or `/usr/local`
|
8
|
+
|
9
|
+
Now `extconf.rb` only checks for `cblas.h` (or `mkl.h` if you are using Intel MKL), `-lcaffe` and `caffe/caffe.hpp`, other dependent headers of `caffe` (such as `leveldb`) should be installed in default search path (`/usr/include` or `/usr/local/include`), or you can use `pkg-config` to add additional options in `LDFLAGS` & `CFLAGS` directly (see below)
|
10
|
+
|
11
|
+
There are three options to specify the path of the libraries & headers:
|
12
|
+
|
13
|
+
### `pkg-config` ###
|
14
|
+
|
15
|
+
Ruby `mkmf` provide convenient use of `pkg-config`, which is included in `extconf.rb`. It will search for `caffe.pc` in `pkg-config` search paths (typically `/usr/lib/pkgconfig` and `/usr/local/lib/pkgconfig`)
|
16
|
+
|
17
|
+
However `caffe` doesn't provide a `.pc` file, but you can write one yourself, here is an example:
|
18
|
+
|
19
|
+
```
|
20
|
+
prefix=/usr/local/caffe
|
21
|
+
libdir=${prefix}/lib
|
22
|
+
includedir=${prefix}/include
|
23
|
+
blasinclude=/usr/local/openblas/include
|
24
|
+
otherflags=-DCPU_ONLY
|
25
|
+
|
26
|
+
Name: caffe
|
27
|
+
Description: caffe C++ lib
|
28
|
+
Version: 1.0.0-rc4
|
29
|
+
Libs: -L${libdir} -lcaffe
|
30
|
+
CFlags: ${otherflags} -I${includedir} -I${blasinclude}
|
31
|
+
```
|
32
|
+
|
33
|
+
Note that `pkg-config` directly adds `LDFLAGS` & `CFLAGS`, so you can add flags other than what `extconf.rb` specifies
|
34
|
+
|
35
|
+
### Build Flags ###
|
36
|
+
|
37
|
+
Build flags can be passed to `extconf.rb` to specify the `caffe` path and other configuration:
|
38
|
+
|
39
|
+
- `--with-caffe-dir=<path>`: specify the caffe path, in which `<path>/include` contains the headers and `<path>/lib` contains the library
|
40
|
+
- `--with-caffe-include` & `--with-caffe-lib`: specify the caffe header and lib path separately
|
41
|
+
- `--with-blas-include=<path>`: specify the path that contains the blas headers
|
42
|
+
- `--with-blas-dir=<path>`: the same as `--with-blas-include=<path>/include`
|
43
|
+
- `--enable-gpu` & `--disable-gpu`: specify GPU mode of caffe, enabled by default
|
44
|
+
- `--enable-mkl` & `--disable-mkl`: specify if Intel MKL is used as blas library, if neither flags are set `extconf.rb` will search for `cblas.h` first and then `mkl.h` if fails
|
45
|
+
|
46
|
+
If `rake` is used to build the extension, use:
|
47
|
+
|
48
|
+
```shell
|
49
|
+
$ rake compile -- <build flags>
|
50
|
+
```
|
51
|
+
|
52
|
+
If installing from `gem`, use:
|
53
|
+
|
54
|
+
```shell
|
55
|
+
$ gem install caffe -- <build flags>
|
56
|
+
```
|
57
|
+
|
58
|
+
If installing from `bundler`, use:
|
59
|
+
|
60
|
+
```shell
|
61
|
+
$ bundle config build.caffe <build flags>
|
62
|
+
|
63
|
+
$ bundle install
|
64
|
+
```
|
65
|
+
|
66
|
+
### Put everything in search path ###
|
67
|
+
|
68
|
+
If you put all the headers and libs required in default search path (like `/usr/local` & `/usr`), and use the default setting (with GPU mode) then everything should be ok
|
69
|
+
|
70
|
+
## Build ##
|
71
|
+
|
72
|
+
Now the project is not a gem project, so it can be built by `rake`
|
73
|
+
|
74
|
+
First, use `bundler` to install all the dependencies:
|
75
|
+
|
76
|
+
```shell
|
77
|
+
$ bundle install
|
78
|
+
```
|
79
|
+
|
80
|
+
Then, use `rake` to build:
|
81
|
+
|
82
|
+
```shell
|
83
|
+
$ rake compile -- <build flags>
|
84
|
+
```
|
85
|
+
|
86
|
+
Test with:
|
87
|
+
|
88
|
+
```shell
|
89
|
+
$ rake test
|
90
|
+
```
|
91
|
+
|
92
|
+
or after compilation:
|
93
|
+
|
94
|
+
```shell
|
95
|
+
$ rake spec
|
96
|
+
```
|
97
|
+
|
98
|
+
require the lib with:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
require './lib/caffe'
|
102
|
+
```
|
103
|
+
|
104
|
+
## Author ##
|
105
|
+
|
106
|
+
Tiny Tiny
|
107
|
+
|
108
|
+
## License ##
|
109
|
+
|
110
|
+
MIT
|
data/ext/caffe/blob.cc
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
#include "blob.hpp"
|
2
|
+
#include "util.hpp"
|
3
|
+
#include <rice/Data_Type.hpp>
|
4
|
+
#include <rice/Constructor.hpp>
|
5
|
+
#include <rice/Module.hpp>
|
6
|
+
#include <stdexcept>
|
7
|
+
#include <algorithm>
|
8
|
+
|
9
|
+
using namespace Rice;
|
10
|
+
|
11
|
+
BlobCursor::BlobCursor(Object blob, bool diff) : diff(diff) {
|
12
|
+
ref = from_ruby<Blob *>(blob);
|
13
|
+
}
|
14
|
+
|
15
|
+
void BlobCursor::checkValid() const {
|
16
|
+
int n = indices.size();
|
17
|
+
const std::vector<int> &shape = ref -> shape();
|
18
|
+
if (n > shape.size()) throw std::logic_error("Invalid BlobCursor!");
|
19
|
+
for (int i = 0; i < n; ++i) {
|
20
|
+
if (shape[i] <= indices[i]) {
|
21
|
+
throw std::out_of_range("Index out of range!");
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
BlobCursor BlobCursor::next(int index) const {
|
27
|
+
BlobCursor ret = *this;
|
28
|
+
ret.indices.push_back(index);
|
29
|
+
ret.checkValid();
|
30
|
+
return ret;
|
31
|
+
}
|
32
|
+
|
33
|
+
Object BlobCursor::get(int index) const {
|
34
|
+
BlobCursor next = this -> next(index);
|
35
|
+
if (next.indices.size() == ref -> shape().size()) {
|
36
|
+
return to_ruby(*next.begin());
|
37
|
+
} else {
|
38
|
+
return to_ruby(next);
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
Object BlobCursor::set(int index, Object data) {
|
43
|
+
BlobCursor next = this -> next(index);
|
44
|
+
if (next.indices.size() == ref -> shape().size()) {
|
45
|
+
*next.mbegin() = from_ruby<float>(data);
|
46
|
+
return data;
|
47
|
+
} else {
|
48
|
+
throw std::logic_error("Not implemented!");
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
const float *BlobCursor::begin() {
|
53
|
+
std::vector<int> full = indices;
|
54
|
+
full.resize(ref -> shape().size());
|
55
|
+
const float *base = diff ? ref -> cpu_diff() : ref -> cpu_data();
|
56
|
+
|
57
|
+
return base + ref -> offset(full);
|
58
|
+
}
|
59
|
+
|
60
|
+
const float *BlobCursor::end() {
|
61
|
+
return begin() + count();
|
62
|
+
}
|
63
|
+
|
64
|
+
float *BlobCursor::mbegin() {
|
65
|
+
std::vector<int> full = indices;
|
66
|
+
full.resize(ref -> shape().size());
|
67
|
+
float *base = diff ? ref -> mutable_cpu_diff() : ref -> mutable_cpu_data();
|
68
|
+
|
69
|
+
return base + ref -> offset(full);
|
70
|
+
}
|
71
|
+
|
72
|
+
float *BlobCursor::mend() {
|
73
|
+
return mbegin() + count();
|
74
|
+
}
|
75
|
+
|
76
|
+
int BlobCursor::count() const {
|
77
|
+
const std::vector<int> &shape = ref -> shape();
|
78
|
+
int n = shape.size();
|
79
|
+
int ans = 1;
|
80
|
+
for (int k = indices.size(); k < n; ++k) {
|
81
|
+
ans *= shape[k];
|
82
|
+
}
|
83
|
+
return ans;
|
84
|
+
}
|
85
|
+
|
86
|
+
int BlobCursor::copy(Array data) {
|
87
|
+
int n = std::min(int(data.size()), count());
|
88
|
+
float *base = mbegin();
|
89
|
+
for (int i = 0; i < n; ++i) {
|
90
|
+
base[i] = from_ruby<float>(data[i]);
|
91
|
+
}
|
92
|
+
return n;
|
93
|
+
}
|
94
|
+
|
95
|
+
static Object getBlobData(Object self) {
|
96
|
+
return to_ruby(BlobCursor(self));
|
97
|
+
}
|
98
|
+
|
99
|
+
static Object getBlobDiff(Object self) {
|
100
|
+
return to_ruby(BlobCursor(self, true));
|
101
|
+
}
|
102
|
+
|
103
|
+
static Array getBlobShape(Object self) {
|
104
|
+
const std::vector<int> &shape = from_ruby<Blob *>(self) -> shape();
|
105
|
+
return Array(shape.begin(), shape.end());
|
106
|
+
}
|
107
|
+
|
108
|
+
static Array setBlobShape(Object self, Array shape) {
|
109
|
+
if (shape.size() > 4) {
|
110
|
+
throw std::invalid_argument("Shape dimension exceeds 4!");
|
111
|
+
}
|
112
|
+
Blob *blob = from_ruby<Blob *>(self);
|
113
|
+
blob -> Reshape(arrayToVector<int>(shape));
|
114
|
+
return shape;
|
115
|
+
}
|
116
|
+
|
117
|
+
struct BlobConstructor {
|
118
|
+
static void construct(Object self, Array shape) {
|
119
|
+
if (shape.size() > 4) {
|
120
|
+
throw std::invalid_argument("Shape dimension exceeds 4!");
|
121
|
+
}
|
122
|
+
DATA_PTR(self.value()) = new Blob(arrayToVector<int>(shape));
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
void Init_blob() {
|
127
|
+
Module rb_mCaffe = define_module("Caffe");
|
128
|
+
|
129
|
+
Data_Type<Blob> rb_cBlob = rb_mCaffe
|
130
|
+
.define_class<Blob>("Blob")
|
131
|
+
.define_constructor(BlobConstructor())
|
132
|
+
.define_method("data", getBlobData)
|
133
|
+
.define_method("diff", getBlobDiff)
|
134
|
+
.define_method("shape", getBlobShape)
|
135
|
+
.define_method("shape=", setBlobShape);
|
136
|
+
|
137
|
+
Data_Type<BlobCursor> rb_cCursor = rb_cBlob
|
138
|
+
.define_class<BlobCursor>("Cursor")
|
139
|
+
.define_constructor(Constructor<BlobCursor, Object, bool>())
|
140
|
+
.define_method("[]", &BlobCursor::get)
|
141
|
+
.define_method("[]=", &BlobCursor::set)
|
142
|
+
.define_method("size", &BlobCursor::count)
|
143
|
+
.define_iterator(&BlobCursor::begin, &BlobCursor::end)
|
144
|
+
.define_method("copy_from!", &BlobCursor::copy);
|
145
|
+
}
|
data/ext/caffe/blob.hpp
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#ifndef __BLOB
|
2
|
+
#define __BLOB
|
3
|
+
|
4
|
+
#include <rice/Class.hpp>
|
5
|
+
#include <rice/Array.hpp>
|
6
|
+
#include <caffe/caffe.hpp>
|
7
|
+
#include <vector>
|
8
|
+
#include <stdexcept>
|
9
|
+
|
10
|
+
typedef caffe::Blob<float> Blob;
|
11
|
+
|
12
|
+
class BlobCursor {
|
13
|
+
std::vector<int> indices;
|
14
|
+
Blob *ref;
|
15
|
+
bool diff;
|
16
|
+
void checkValid(void) const;
|
17
|
+
public:
|
18
|
+
BlobCursor(Rice::Object blob, bool diff = false);
|
19
|
+
BlobCursor next(int index) const;
|
20
|
+
Rice::Object get(int index) const;
|
21
|
+
Rice::Object set(int index, Rice::Object data);
|
22
|
+
const float *begin(void);
|
23
|
+
const float *end(void);
|
24
|
+
float *mbegin(void);
|
25
|
+
float *mend(void);
|
26
|
+
int count(void) const;
|
27
|
+
int copy(Rice::Array data);
|
28
|
+
};
|
29
|
+
|
30
|
+
void Init_blob(void);
|
31
|
+
|
32
|
+
#endif
|
data/ext/caffe/caffe.cc
ADDED
data/ext/caffe/common.cc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#include "common.hpp"
|
2
|
+
#include <rice/Module.hpp>
|
3
|
+
#include <rice/Class.hpp>
|
4
|
+
#include <caffe/caffe.hpp>
|
5
|
+
|
6
|
+
using namespace Rice;
|
7
|
+
|
8
|
+
#define EnumCast(type, classname) template<> \
|
9
|
+
type from_ruby<type>(Object self) { \
|
10
|
+
int num = from_ruby<int>(self.call("to_i")); \
|
11
|
+
return type(num); \
|
12
|
+
} \
|
13
|
+
template<> \
|
14
|
+
Object to_ruby<type>(const type &self) { \
|
15
|
+
int num = int(self); \
|
16
|
+
Object cl = (classname); \
|
17
|
+
return cl.call("fetch", num); \
|
18
|
+
}
|
19
|
+
|
20
|
+
EnumCast(caffe::Phase, define_module("Caffe")
|
21
|
+
.const_get("Phase"));
|
22
|
+
EnumCast(caffe::Caffe::Brew, Class(define_module("Caffe")
|
23
|
+
.const_get("SolverParameter"))
|
24
|
+
.const_get("SolverMode"));
|
25
|
+
|
26
|
+
void Init_common() {
|
27
|
+
Module rb_mCaffe = define_module("Caffe")
|
28
|
+
.define_module_function("mode", caffe::Caffe::mode)
|
29
|
+
.define_module_function("mode=", caffe::Caffe::set_mode)
|
30
|
+
.define_module_function("solver_count", caffe::Caffe::solver_count)
|
31
|
+
.define_module_function("solver_count=", caffe::Caffe::set_solver_count)
|
32
|
+
.define_module_function("solver_rank", caffe::Caffe::solver_rank)
|
33
|
+
.define_module_function("solver_rank=", caffe::Caffe::set_solver_rank)
|
34
|
+
.define_module_function("multiprocess", caffe::Caffe::multiprocess)
|
35
|
+
.define_module_function("multiprocess=", caffe::Caffe::set_multiprocess);
|
36
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#ifndef __COMMON
|
2
|
+
#define __COMMON
|
3
|
+
|
4
|
+
#include <caffe/caffe.hpp>
|
5
|
+
#include <rice/Data_Type.hpp>
|
6
|
+
|
7
|
+
#define DefineEnumCast(type) template<> \
|
8
|
+
type from_ruby<type>(Rice::Object); \
|
9
|
+
template<> \
|
10
|
+
Rice::Object to_ruby<type>(const type &)
|
11
|
+
|
12
|
+
DefineEnumCast(caffe::Phase);
|
13
|
+
DefineEnumCast(caffe::Caffe::Brew);
|
14
|
+
|
15
|
+
void Init_common(void);
|
16
|
+
|
17
|
+
#endif
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path '../mkmf_cxx', __FILE__
|
2
|
+
|
3
|
+
@libdir_basename = 'lib'
|
4
|
+
|
5
|
+
dir_config 'caffe'
|
6
|
+
dir_config 'blas'
|
7
|
+
|
8
|
+
checking_for 'caffe.pc' do
|
9
|
+
pkg_config 'caffe'
|
10
|
+
end
|
11
|
+
|
12
|
+
$libs = ''
|
13
|
+
|
14
|
+
$defs.push '-DCPU_ONLY' unless enable_config 'gpu', true
|
15
|
+
|
16
|
+
mkl = enable_config 'mkl', nil
|
17
|
+
|
18
|
+
if mkl == true || !have_header('cblas.h')
|
19
|
+
$defs.push '-DUSE_MKL'
|
20
|
+
if mkl == false || !have_header('mkl.h')
|
21
|
+
puts <<MSG
|
22
|
+
blas header not found.
|
23
|
+
use build flag "--with-blas-dir=/path/to/blas" to specify the blas path.
|
24
|
+
or use "--with-blas-include" to specify the include path.
|
25
|
+
MSG
|
26
|
+
raise
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
unless library?('caffe') && header_cxx?('caffe/caffe.hpp')
|
31
|
+
puts <<MSG
|
32
|
+
caffe not found.
|
33
|
+
use build flag "--with-caffe-dir=/path/to/caffe" to specify the caffe path.
|
34
|
+
or use "--with-caffe-include" & "--with-caffe-lib" to specify include path & lib path separately.
|
35
|
+
MSG
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
create_makefile 'caffe/caffe'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'mkmf-rice'
|
2
|
+
|
3
|
+
$CXXFLAGS = ''
|
4
|
+
|
5
|
+
TRY_LINK.sub!(/^#{Regexp.quote($CXX)}/, '$(CXX)')
|
6
|
+
$LDSHARED_CXX.sub!(/^#{Regexp.quote($CXX)}/, '$(CXX)')
|
7
|
+
$CXX = '$(CXX)'
|
8
|
+
|
9
|
+
if CONFIG['target_os'] == 'mswin32' && $RICE_USING_MINGW32
|
10
|
+
init_rice_mkmf_cross_compile_mingw2_for_vc6
|
11
|
+
end
|
12
|
+
|
13
|
+
def configuration(srcdir)
|
14
|
+
configuration = configuration_orig(srcdir)
|
15
|
+
configuration.each do |config|
|
16
|
+
# Make sure we link the extension using the C++ compiler
|
17
|
+
config.gsub!(/^LDSHARED\s*=.*$/, "LDSHARED = #{$LDSHARED_CXX}")
|
18
|
+
|
19
|
+
# Make sure set the C++ flags correctly
|
20
|
+
config.gsub!(/^CXXFLAGS\s*=.*$/, "CXXFLAGS = $(CFLAGS) #{$CXXFLAGS}")
|
21
|
+
end
|
22
|
+
configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
RbConfig::CONFIG['CXXPP'] = RbConfig::CONFIG['CXX'] + ' -E'
|
26
|
+
CONFTEST_CC = "#{CONFTEST}.cc".freeze
|
27
|
+
|
28
|
+
def create_tmpsrc_cxx(src)
|
29
|
+
src = "#{COMMON_HEADERS}\n#{src}"
|
30
|
+
src = yield(src) if block_given?
|
31
|
+
src.gsub!(/[ \t]+$/, '')
|
32
|
+
src.gsub!(/\A\n+|^\n+$/, '')
|
33
|
+
src.sub!(/[^\n]\z/, "\\&\n")
|
34
|
+
count = 0
|
35
|
+
begin
|
36
|
+
open(CONFTEST_CC, 'wb') do |cfile|
|
37
|
+
cfile.print src
|
38
|
+
end
|
39
|
+
rescue Errno::EACCES
|
40
|
+
if (count += 1) < 5
|
41
|
+
sleep 0.2
|
42
|
+
retry
|
43
|
+
end
|
44
|
+
end
|
45
|
+
src
|
46
|
+
end
|
47
|
+
|
48
|
+
def try_do_cxx(src, command, *opts, &b)
|
49
|
+
unless have_devel?
|
50
|
+
raise <<MSG
|
51
|
+
The compiler failed to generate an executable file.
|
52
|
+
You have to install development tools first.
|
53
|
+
MSG
|
54
|
+
end
|
55
|
+
begin
|
56
|
+
src = create_tmpsrc_cxx(src, &b)
|
57
|
+
xsystem(command, *opts)
|
58
|
+
ensure
|
59
|
+
log_src(src)
|
60
|
+
MakeMakefile.rm_rf "#{CONFTEST}.dSYM"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def link_command(ldflags, opt = '', libpath = $DEFLIBPATH | $LIBPATH)
|
65
|
+
librubyarg = $extmk ? $LIBRUBYARG_STATIC : '$(LIBRUBYARG)'
|
66
|
+
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote,
|
67
|
+
'src' => CONFTEST_CC,
|
68
|
+
'arch_hdrdir' => $arch_hdrdir.quote,
|
69
|
+
'top_srcdir' => $top_srcdir.quote,
|
70
|
+
'INCFLAGS' => $INCFLAGS,
|
71
|
+
'CPPFLAGS' => $CPPFLAGS,
|
72
|
+
'CXXFLAGS' => $CXXFLAGS,
|
73
|
+
'CFLAGS' => $CFLAGS,
|
74
|
+
'ARCH_FLAG' => $ARCH_FLAG,
|
75
|
+
'LDFLAGS' => "#{$LDFLAGS} #{ldflags}",
|
76
|
+
'LOCAL_LIBS' => "#{$LOCAL_LIBS} #{$libs}",
|
77
|
+
'LIBS' => "#{librubyarg} #{opt} #{$LIBS}")
|
78
|
+
conf['LIBPATH'] = libpathflag(libpath.map do |s|
|
79
|
+
RbConfig.expand(s.dup, conf)
|
80
|
+
end)
|
81
|
+
RbConfig.expand(TRY_LINK.dup, conf)
|
82
|
+
end
|
83
|
+
|
84
|
+
def try_link0(src, opt = '', *opts, &b)
|
85
|
+
cmd = link_command('', opt)
|
86
|
+
if $universal
|
87
|
+
require 'tmpdir'
|
88
|
+
Dir.mktmpdir('mkmf_', oldtmpdir = ENV['TMPDIR']) do |tmpdir|
|
89
|
+
begin
|
90
|
+
ENV['TMPDIR'] = tmpdir
|
91
|
+
try_do_cxx(src, cmd, *opts, &b)
|
92
|
+
ensure
|
93
|
+
ENV['TMPDIR'] = oldtmpdir
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
try_do_cxx(src, cmd, *opts, &b)
|
98
|
+
end && File.executable?(CONFTEST + $EXEEXT)
|
99
|
+
end
|
100
|
+
|
101
|
+
def cxxpp_command(outfile, opt = '')
|
102
|
+
conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote,
|
103
|
+
'srcdir' => $srcdir.quote,
|
104
|
+
'arch_hdrdir' => $arch_hdrdir.quote,
|
105
|
+
'top_srcdir' => $top_srcdir.quote)
|
106
|
+
if $universal && (arch_flag = conf['ARCH_FLAG']) && !arch_flag.empty?
|
107
|
+
conf['ARCH_FLAG'] = arch_flag.gsub(/(?:\G|\s)-arch\s+\S+/, '')
|
108
|
+
end
|
109
|
+
flags = [$INCFLAGS, $CPPFLAGS, $CFLAGS, opt].join ' '
|
110
|
+
RbConfig.expand("$(CXXPP) #{flags} #{CONFTEST_CC} #{outfile}", conf)
|
111
|
+
end
|
112
|
+
|
113
|
+
def try_cxxpp(src, opt = '', *opts, &b)
|
114
|
+
try_do_cxx(src, cxxpp_command(CPPOUTFILE, opt), *opts, &b) &&
|
115
|
+
File.file?('conftest.i')
|
116
|
+
ensure
|
117
|
+
MakeMakefile.rm_f 'conftest*'
|
118
|
+
end
|
119
|
+
|
120
|
+
def trans_opt(opt)
|
121
|
+
[[:to_str], [:join, ' '], [:to_s]].each do |meth, *args|
|
122
|
+
return opt.send(meth, *args) if opt.respond_to?(meth)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def header_cxx?(header, preheaders = nil, opt = '', &b)
|
127
|
+
opt = "#{trans_opt opt} #{trans_opt $defs}"
|
128
|
+
checking_for header do
|
129
|
+
if try_cxxpp(cpp_include(preheaders) + cpp_include(header), opt, &b)
|
130
|
+
$defs.push(format('-DHAVE_%s', header.tr_cpp))
|
131
|
+
true
|
132
|
+
else
|
133
|
+
false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def try_library(libs, opt = '', &b)
|
139
|
+
opt = "#{trans_opt opt} #{libs}"
|
140
|
+
try_link(MAIN_DOES_NOTHING, opt, &b)
|
141
|
+
end
|
142
|
+
|
143
|
+
def library?(lib, opt = '', &b)
|
144
|
+
lib = with_config(lib + 'lib', lib)
|
145
|
+
checking_for format(LIBARG, lib) do
|
146
|
+
if COMMON_LIBS.include?(lib)
|
147
|
+
true
|
148
|
+
else
|
149
|
+
libs = append_library($libs, lib)
|
150
|
+
if try_library(libs, opt, &b)
|
151
|
+
$libs = libs
|
152
|
+
true
|
153
|
+
else
|
154
|
+
false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|