-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtmpl-source.go
137 lines (117 loc) · 4.14 KB
/
tmpl-source.go
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
package main
import (
"bytes"
"crypto/md5"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
"text/template"
"github.com/forj-oss/forjj-modules/trace"
)
// TmplSource represent a file source information as declared in templates.yaml
type TmplSource struct {
Chmod os.FileMode
// One of the following field must be set.
Template string // Define a template file.
Source string // Define a source file.
Built string // Define a built file.
Generated string // Define a source file generated by a build task. The generated source file is then copied like Source.
md5Generated []byte // if file exist, contains the md5sum of the source file before any change.
Tag string // template string to use.
If string `yaml:"if"` // If `If` is empty, the file will be ignored. otherwise the file will copied/generated
// as usual.
}
// Generate read a source file and interpret it with text/template.
// It reads the source file, generate the final file and state if file generated has been updated or not.
//
// It permits to use another template tag to avoid conflict with the code to generate.
// This Tag have to define a pair string. Ex: "(())" the string is split in 2, to define the begin and end tag. ie begin="((", end="))"
// "((" is invalid because begin == end == "("
// "[[]" is also invalid, because the tag size is not divided per 2 (size is 3)
//
// When a different tag is used, any existing {{}} in source file will be kept after the template generation.
func (ts *TmplSource) Generate(tmpl_data interface{}, template_dir, dest_path, dest_name string) (updated bool, _ error) {
src := path.Join(template_dir, ts.Template)
dest := path.Join(dest_path, dest_name)
parent := path.Dir(dest)
if parent != "." {
if _, err := os.Stat(parent); err != nil {
os.MkdirAll(parent, 0755)
updated = true
}
}
var data string
if b, err := ioutil.ReadFile(src); err != nil {
return false, fmt.Errorf("Load issue. %s", err)
} else {
data = string(b)
}
if ts.Tag != "" {
if v := len(ts.Tag); v < 4 {
return false, fmt.Errorf("%s: tag template string must have at least 2 cars for 'begin' and 'end' tag. '%s' is too small", ts.Template, ts.Tag)
}
if v := len(ts.Tag); v%2 != 0 {
return false, fmt.Errorf("%s: tag template string must define begin and end different tag, each of same size. Got %d", ts.Template, v)
}
tagSize := len(ts.Tag) / 2
tag1 := ts.Tag[0:tagSize]
tag2 := ts.Tag[tagSize:]
if tag1 == tag2 {
return false, fmt.Errorf("%s: tag template string (%s) must define different string for begin/end tag. Got begin='%s' vs end='%s'",
ts.Template, ts.Tag, tag1, tag2)
}
if tag1 == "{{" || tag1 == "}}" {
return false, fmt.Errorf("%s: begin tag '%s' cannot be '{{' or '}}'", ts.Template, tag1)
}
if tag2 == "{{" || tag2 == "}}" {
return false, fmt.Errorf("%s: end tag '%s' cannot be '{{' or '}}'", ts.Template, tag2)
}
gotrace.Trace("Tag: begin='%s', end='%s'", tag1, tag2)
replacer := map[string]string{
"}}": tag1 + "`}}`" + tag2,
"{{": tag1 + "`{{`" + tag2,
tag1: "{{",
tag2: "}}",
}
for _, tagSel := range []string{"{{", "}}", tag1, tag2} {
data = strings.Replace(data, tagSel, replacer[tagSel], -1)
}
}
data = strings.Replace(data, "}}\\\n", "}}", -1)
t, err := template.New(src).Funcs(template.FuncMap{
"lookup": lookup,
}).Parse(data)
if err != nil {
return false, fmt.Errorf("Template issue. %s", err)
}
orig_md5, _ := md5sum(dest)
final_md5_file := md5.New()
if out, err := os.Create(dest); err != nil {
return false, fmt.Errorf("Unable to create %s. %s", dest, err)
} else {
multi_write_file := io.MultiWriter(out, final_md5_file)
if err := t.Execute(multi_write_file, tmpl_data); err != nil {
return false, fmt.Errorf("Unable to interpret %s. %s", dest, err)
}
out.Close()
}
final_md5 := final_md5_file.Sum(nil)
if orig_md5 != nil {
updated = updated || !bytes.Equal(orig_md5, final_md5)
} else {
updated = true
}
if u, err := set_rights(dest, ts.Chmod); err != nil {
return false, fmt.Errorf("%s", err)
} else {
updated = updated || u
}
return
}
// Store generated source md5
func (ts *TmplSource) storeMD5(src string) {
ts.md5Generated, _ = md5sum(src)
}