github.com/hwaf/hwaf@v0.0.0-20140814122253-5465f73b20f1/hlib/render.go (about)

     1  package hlib
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"reflect"
     8  	"sort"
     9  	"strconv"
    10  	"strings"
    11  	"text/template"
    12  )
    13  
    14  type HscriptPyEncoder struct {
    15  	w io.Writer
    16  }
    17  
    18  func NewHscriptPyEncoder(w io.Writer) *HscriptPyEncoder {
    19  	return &HscriptPyEncoder{w: w}
    20  }
    21  
    22  func (enc *HscriptPyEncoder) Encode(wscript *Wscript_t) error {
    23  	var err error
    24  
    25  	_, err = fmt.Fprintf(
    26  		enc.w,
    27  		`## -*- python -*-
    28  ## automatically generated from a hscript
    29  ## do NOT edit.
    30  
    31  ## waf imports
    32  import waflib.Logs as msg
    33  `,
    34  	)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	// generate package header
    40  	const pkg_hdr_tmpl = `
    41  PACKAGE = {
    42      "name":    "{{.Name}}",
    43      "authors": {{.Authors | as_pylist}},
    44  {{if .Managers}}    "managers": {{.Managers | as_pylist}},{{end}}
    45  {{if .Version}}    "version":  "{{.Version}}",{{end}}
    46  }
    47  
    48  ### ---------------------------------------------------------------------------
    49  def pkg_deps(ctx):
    50      {{. | gen_wscript_pkg_deps}}
    51      return # pkg_deps
    52  `
    53  	err = w_tmpl(enc.w, pkg_hdr_tmpl, wscript.Package)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	// generate options - section
    59  	err = w_tmpl(
    60  		enc.w,
    61  		`
    62  
    63  ### ---------------------------------------------------------------------------
    64  def options(ctx):
    65      {{with .Tools}}{{. | gen_wscript_tools}}{{end}}
    66      return # options
    67  `,
    68  		wscript.Options,
    69  	)
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	// generate configure - section
    75  	err = w_tmpl(
    76  		enc.w,
    77  		`
    78  
    79  ### ---------------------------------------------------------------------------
    80  def configure(ctx):
    81      {{with .Tools}}{{. | gen_wscript_tools}}{{end}}
    82      {{with .HwafCall}}{{. | gen_wscript_hwaf_call}}
    83      {{end}}
    84      {{.Env | gen_wscript_env}}
    85      {{range .Stmts}}##{{. | gen_wscript_stmts}}
    86      {{end}}
    87      return # configure
    88  `,
    89  		wscript.Configure,
    90  	)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	// generate build - section
    96  	err = w_tmpl(
    97  		enc.w,
    98  		`
    99  
   100  ### ---------------------------------------------------------------------------
   101  def build(ctx):
   102      {{with .Tools}}{{. | gen_wscript_tools}}{{end}}
   103      {{with .HwafCall}}{{. | gen_wscript_hwaf_call}}
   104      {{end}}
   105      {{range .Stmts}}##{{. | gen_wscript_stmts}}
   106      {{end}}
   107      {{with .Targets}}{{. | gen_wscript_targets}}{{end}}
   108      return # build
   109  `,
   110  		wscript.Build,
   111  	)
   112  	if err != nil {
   113  		return err
   114  	}
   115  
   116  	_, err = fmt.Fprintf(
   117  		enc.w,
   118  		"\n## EOF ##\n",
   119  	)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	return err
   125  }
   126  
   127  func w_tmpl(w io.Writer, text string, data interface{}) error {
   128  	t := template.New("wscript")
   129  	t.Funcs(template.FuncMap{
   130  		"trim": strings.TrimSpace,
   131  		"as_pylist": func(alist interface{}) string {
   132  			rv := reflect.ValueOf(alist)
   133  			str := make([]string, 0, rv.Len())
   134  			for i := 0; i < rv.Len(); i++ {
   135  				s := rv.Index(i)
   136  				str = append(str, fmt.Sprintf("%q", s))
   137  			}
   138  			return "[" + strings.Join(str, ", ") + "]"
   139  		},
   140  		"gen_wscript_pkg_deps":  gen_wscript_pkg_deps,
   141  		"gen_wscript_tools":     gen_wscript_tools,
   142  		"gen_wscript_hwaf_call": gen_wscript_hwaf_call,
   143  		"gen_wscript_env":       gen_wscript_env,
   144  		"gen_wscript_stmts":     gen_wscript_stmts,
   145  		"gen_wscript_targets":   gen_wscript_targets,
   146  	})
   147  	template.Must(t.Parse(text))
   148  	return t.Execute(w, data)
   149  }
   150  
   151  func w_gen_taglist(tags string) []string {
   152  	str := make([]string, 0, strings.Count(tags, "&"))
   153  	for _, v := range strings.Split(tags, "&") {
   154  		v = strings.Trim(v, " ")
   155  		if len(v) > 0 {
   156  			str = append(str, v)
   157  		}
   158  	}
   159  	return str
   160  }
   161  
   162  func w_gen_valdict_switch_str(indent string, values [][2]string) string {
   163  	o := make([]string, 0, len(values))
   164  	o = append(o, "(")
   165  	for _, v := range values {
   166  		tags := w_gen_taglist(v[0])
   167  		key_fmt := "(%s)"
   168  		if strings.Count(v[0], "&") <= 0 {
   169  			key_fmt = "%s"
   170  		}
   171  		val_fmt := "%s"
   172  		if strings.Count(v[1], ",") > 0 {
   173  			val_fmt = "[%s]"
   174  		}
   175  		if len(v[1]) == 0 {
   176  			val_fmt = "%q"
   177  		}
   178  
   179  		o = append(o,
   180  			fmt.Sprintf(
   181  				"%s  {%s: %s},",
   182  				indent,
   183  				fmt.Sprintf(key_fmt, w_py_strlist(tags)),
   184  				fmt.Sprintf(val_fmt, v[1]),
   185  			),
   186  		)
   187  	}
   188  	o = append(o, indent+")")
   189  	return strings.Join(o, "\n")
   190  }
   191  
   192  func w_py_hlib_value(indent string, fctname string, x Value) []string {
   193  	str := make([]string, 0)
   194  
   195  	values := make([][2]string, 0, len(x.Set))
   196  	for _, v := range x.Set {
   197  		k := v.Tag
   198  		values = append(values, [2]string{k, w_py_strlist(v.Value)})
   199  	}
   200  	str = append(
   201  		str,
   202  		fmt.Sprintf(
   203  			"ctx.%s(%q, %s)",
   204  			fctname,
   205  			x.Name,
   206  			w_gen_valdict_switch_str(indent, values),
   207  		),
   208  	)
   209  
   210  	return str
   211  }
   212  
   213  func w_py_strlist(str []string) string {
   214  	o := make([]string, 0, len(str))
   215  	for _, v := range str {
   216  		vv, err := strconv.Unquote(v)
   217  		if err != nil {
   218  			vv = v
   219  		}
   220  		if strings.HasPrefix(vv, `"`) && strings.HasSuffix(vv, `"`) {
   221  			if len(vv) > 1 {
   222  				vv = vv[1 : len(vv)-1]
   223  			}
   224  		}
   225  		o = append(o, fmt.Sprintf("%q", vv))
   226  	}
   227  	return strings.Join(o, ", ")
   228  }
   229  
   230  func gen_wscript_pkg_deps(pkg Package_t) string {
   231  	const indent = "    "
   232  	var str []string
   233  	public_deps := make([]Dep_t, 0, len(pkg.Deps))
   234  	private_deps := make([]Dep_t, 0, len(pkg.Deps))
   235  	runtime_deps := make([]Dep_t, 0, len(pkg.Deps))
   236  	for _, dep := range pkg.Deps {
   237  		if dep.Type.HasMask(RuntimeDep) {
   238  			runtime_deps = append(runtime_deps, dep)
   239  		}
   240  		if dep.Type.HasMask(PublicDep) {
   241  			public_deps = append(public_deps, dep)
   242  		}
   243  		if dep.Type.HasMask(PrivateDep) {
   244  			private_deps = append(private_deps, dep)
   245  		}
   246  	}
   247  
   248  	str = append(str, "")
   249  	if len(public_deps) > 0 {
   250  		str = append(str, "## public dependencies")
   251  		for _, dep := range public_deps {
   252  			str = append(
   253  				str,
   254  				fmt.Sprintf(
   255  					"ctx.use_pkg(%q, version=%q, public=True)",
   256  					dep.Name,
   257  					dep.Version,
   258  				),
   259  			)
   260  		}
   261  		str = append(str, "")
   262  	} else {
   263  		str = append(str, "## no public dependencies")
   264  	}
   265  
   266  	if len(private_deps) > 0 {
   267  		str = append(str, "## private dependencies")
   268  		for _, dep := range private_deps {
   269  			str = append(
   270  				str,
   271  				fmt.Sprintf(
   272  					"ctx.use_pkg(%q, version=%q, private=True)",
   273  					dep.Name,
   274  					dep.Version,
   275  				),
   276  			)
   277  		}
   278  		str = append(str, "")
   279  	} else {
   280  		str = append(str, "## no private dependencies")
   281  	}
   282  
   283  	if len(runtime_deps) > 0 {
   284  		str = append(str, "## runtime dependencies")
   285  		for _, dep := range runtime_deps {
   286  			str = append(
   287  				str,
   288  				fmt.Sprintf(
   289  					"ctx.use_pkg(%q, version=%q, runtime=True)",
   290  					dep.Name,
   291  					dep.Version,
   292  				),
   293  			)
   294  		}
   295  	} else {
   296  		str = append(str, "## no runtime dependencies")
   297  	}
   298  
   299  	// reindent:
   300  	for i, s := range str[1:] {
   301  		str[i+1] = indent + s
   302  	}
   303  
   304  	return strings.Join(str, "\n")
   305  }
   306  
   307  func gen_wscript_tools(tools []string) string {
   308  	const indent = "    "
   309  	str := []string{""}
   310  
   311  	for _, tool := range tools {
   312  		str = append(
   313  			str,
   314  			fmt.Sprintf("ctx.load(%q)", tool),
   315  			fmt.Sprintf("try: ctx.%s()", tool),
   316  			"except AttributeError: pass",
   317  			"",
   318  		)
   319  	}
   320  
   321  	// reindent:
   322  	for i, s := range str[1:] {
   323  		str[i+1] = indent + s
   324  	}
   325  
   326  	return strings.Join(str, "\n")
   327  }
   328  
   329  func gen_wscript_hwaf_call(calls []string) string {
   330  	const indent = "    "
   331  	str := []string{""}
   332  
   333  	for _, script := range calls {
   334  		str = append(
   335  			str,
   336  			fmt.Sprintf(
   337  				"ctx._hwaf_load_fct(PACKAGE['name'], %q)",
   338  				script,
   339  			),
   340  			"",
   341  		)
   342  	}
   343  
   344  	// reindent:
   345  	for i, s := range str[1:] {
   346  		str[i+1] = indent + s
   347  	}
   348  
   349  	return strings.Join(str, "\n")
   350  }
   351  
   352  func gen_wscript_env(env Env_t) string {
   353  	const indent = "    "
   354  	var str []string
   355  
   356  	//str = append(str, "## environment -- begin")
   357  	for k := range env {
   358  		str = append(str, fmt.Sprintf("ctx.hwaf_declare_runtime_env(%q)", k))
   359  		// 	switch len(values) {
   360  		// 	case 0:
   361  		// 	case 1:
   362  		// 		if len(values.Set) > 1 {
   363  		// 			// str = append(
   364  		// 			// 	str,
   365  		// 			// 	fmt.Sprintf("%s: {"),
   366  		// 			// )
   367  		// 		} else {
   368  		// 			kvs := values.Set[0]
   369  		// 			for k, v := range kvs {
   370  
   371  		// 			}
   372  		// 		}
   373  		// 	default:
   374  		// 	}
   375  	}
   376  	//str = append(str, "## environment -- end")
   377  
   378  	// // reindent:
   379  	// for i, s := range str[1:] {
   380  	// 	str[i+1] = indent + s
   381  	// }
   382  
   383  	return strings.Join(str, "\n")
   384  }
   385  
   386  func gen_wscript_stmts(stmt Stmt) string {
   387  	const indent = "    "
   388  	var str []string
   389  	switch x := stmt.(type) {
   390  	case *AliasStmt:
   391  		str = []string{fmt.Sprintf("## alias %v", stmt)}
   392  		str = append(
   393  			str,
   394  			w_py_hlib_value(indent, "hwaf_declare_runtime_alias", x.Value)...,
   395  		)
   396  
   397  	case *MacroStmt:
   398  		str = []string{fmt.Sprintf("## macro %v", stmt)}
   399  		str = append(
   400  			str,
   401  			w_py_hlib_value(indent, "hwaf_declare_macro", x.Value)...,
   402  		)
   403  
   404  	case *MacroAppendStmt:
   405  		str = []string{fmt.Sprintf("## macro_append %v", stmt)}
   406  		str = append(
   407  			str,
   408  			w_py_hlib_value(indent, "hwaf_macro_append", x.Value)...,
   409  		)
   410  
   411  	case *MacroPrependStmt:
   412  		str = []string{fmt.Sprintf("## macro_prepend %v", stmt)}
   413  		str = append(
   414  			str,
   415  			w_py_hlib_value(indent, "hwaf_macro_prepend", x.Value)...,
   416  		)
   417  
   418  	case *MacroRemoveStmt:
   419  		str = []string{fmt.Sprintf("## macro_remove %v", stmt)}
   420  		str = append(
   421  			str,
   422  			w_py_hlib_value(indent, "hwaf_macro_remove", x.Value)...,
   423  		)
   424  
   425  	case *SetStmt:
   426  		str = []string{fmt.Sprintf("## set %v", stmt)}
   427  		str = append(
   428  			str,
   429  			w_py_hlib_value(indent, "hwaf_declare_macro", x.Value)...,
   430  		)
   431  		str = append(
   432  			str,
   433  			fmt.Sprintf("ctx.hwaf_declare_runtime_env(%q)", x.Value.Name),
   434  		)
   435  
   436  	case *SetPrependStmt:
   437  		str = []string{fmt.Sprintf("## set_prepend %v", stmt)}
   438  		str = append(
   439  			str,
   440  			w_py_hlib_value(indent, "hwaf_macro_prepend", x.Value)...,
   441  		)
   442  
   443  	case *SetRemoveStmt:
   444  		str = []string{fmt.Sprintf("## set_remove %v", stmt)}
   445  		str = append(
   446  			str,
   447  			w_py_hlib_value(indent, "hwaf_macro_remove", x.Value)...,
   448  		)
   449  	case *TagStmt:
   450  		str = []string{fmt.Sprintf("## tag %v", stmt)}
   451  		values := w_py_strlist(x.Content)
   452  		str = append(str,
   453  			"ctx.hwaf_declare_tag(",
   454  			fmt.Sprintf("%s%q,", indent, x.Name),
   455  			fmt.Sprintf("%scontent=[%s]", indent, values),
   456  			")",
   457  		)
   458  
   459  	case *ApplyTagStmt:
   460  		str = []string{fmt.Sprintf("## apply_tag %v", stmt)}
   461  		if len(x.Value.Set) > 0 {
   462  			str = append(
   463  				str,
   464  				fmt.Sprintf("ctx.hwaf_apply_tag(%v)", w_py_strlist(x.Value.Set[0].Value)),
   465  			)
   466  		} else {
   467  			str = append(
   468  				str,
   469  				fmt.Sprintf("ctx.hwaf_apply_tag(%q)", x.Value.Name),
   470  			)
   471  		}
   472  
   473  	case *PathStmt:
   474  		str = []string{fmt.Sprintf("## path %v", stmt)}
   475  		str = append(
   476  			str,
   477  			w_py_hlib_value(indent, "hwaf_declare_path", x.Value)...,
   478  		)
   479  
   480  	case *PathAppendStmt:
   481  		str = []string{fmt.Sprintf("## path_append %v", stmt)}
   482  		str = append(
   483  			str,
   484  			w_py_hlib_value(indent, "hwaf_path_append", x.Value)...,
   485  		)
   486  
   487  	case *PathPrependStmt:
   488  		str = []string{fmt.Sprintf("## path_prepend %v", stmt)}
   489  		str = append(
   490  			str,
   491  			w_py_hlib_value(indent, "hwaf_path_prepend", x.Value)...,
   492  		)
   493  
   494  	case *PathRemoveStmt:
   495  		str = []string{fmt.Sprintf("## path_remove %v", stmt)}
   496  		str = append(
   497  			str,
   498  			w_py_hlib_value(indent, "hwaf_path_remove", x.Value)...,
   499  		)
   500  
   501  	//case *ApplyPatternStmt:
   502  
   503  	default:
   504  		str = []string{fmt.Sprintf("### **** statement %T (%v)", stmt, stmt)}
   505  	}
   506  
   507  	// reindent:
   508  	for i, s := range str[1:] {
   509  		str[i+1] = indent + s
   510  	}
   511  
   512  	return strings.Join(str, "\n")
   513  }
   514  
   515  func gen_wscript_targets(tgts Targets_t) string {
   516  	const indent = "    "
   517  	var str []string
   518  
   519  	// sort targets by name.
   520  	// waf uses the task order as an internal id.
   521  	// to make incremental builds to work properly, we need to
   522  	// always create the same wscript from a given hscript.yml file.
   523  	sort.Sort(tgts)
   524  
   525  	cnv_values := func(values []Value) []string {
   526  		out := make([]string, 0, len(values))
   527  		for _, v := range values {
   528  			if len(v.Set) > 0 {
   529  				// FIXME what about the non-default values ??
   530  				out = append(out, v.Set[0].Value...)
   531  			} else {
   532  				fmt.Fprintf(os.Stderr, "**warn** in target: %v\n", tgts)
   533  				fmt.Fprintf(os.Stderr, "**warn** invalid value: %v\n", v)
   534  			}
   535  		}
   536  		return out
   537  	}
   538  
   539  	for i, tgt := range tgts {
   540  		if i != 0 {
   541  			str = append(str, "")
   542  		}
   543  		srcs := cnv_values(tgt.Source)
   544  		features := strings.Join(tgt.Features, " ")
   545  		target_name := tgt.Target
   546  		if target_name == "" {
   547  			target_name = tgt.Name
   548  		}
   549  		str = append(str,
   550  			"ctx(",
   551  			fmt.Sprintf("%sfeatures = %q,", indent, features),
   552  			fmt.Sprintf("%sname     = %q,", indent, tgt.Name),
   553  			fmt.Sprintf("%starget   = %q,", indent, target_name),
   554  			fmt.Sprintf("%ssource   = [%s],", indent, w_py_strlist(srcs)),
   555  		)
   556  
   557  		if tgt.Group != "" {
   558  			str = append(
   559  				str,
   560  				fmt.Sprintf("%sgroup    = %q,", indent, tgt.Group),
   561  			)
   562  		}
   563  
   564  		for _, vv := range []struct {
   565  			hdr    string
   566  			values []Value
   567  		}{
   568  			{"use", tgt.Use},
   569  			{"defines", tgt.Defines},
   570  			{"cflags", tgt.CFlags},
   571  			{"cxxflags", tgt.CxxFlags},
   572  			{"linkflags", tgt.LinkFlags},
   573  			{"shlibflags", tgt.ShlibFlags},
   574  			{"stlibflags", tgt.StlibFlags},
   575  			{"rpath", tgt.RPath},
   576  			{"includes", tgt.Includes},
   577  			{"export_includes", tgt.ExportIncludes},
   578  			{"install_path", tgt.InstallPath},
   579  		} {
   580  			if len(vv.values) > 0 {
   581  				vals := cnv_values(vv.values)
   582  				str = append(str,
   583  					fmt.Sprintf(
   584  						"%s%s = [%s],",
   585  						indent,
   586  						vv.hdr,
   587  						w_py_strlist(vals),
   588  					),
   589  				)
   590  			}
   591  		}
   592  
   593  		if len(tgt.Env) > 0 {
   594  			str = append(str,
   595  				fmt.Sprintf("%senv = ctx._hwaf_subenv({", indent),
   596  			)
   597  			for hdr, value := range tgt.Env {
   598  				vals := cnv_values([]Value{value})
   599  				str = append(str,
   600  					fmt.Sprintf(
   601  						"%s'%s': [%s],",
   602  						indent+indent,
   603  						hdr,
   604  						w_py_strlist(vals),
   605  					),
   606  				)
   607  			}
   608  			str = append(str,
   609  				indent+"}),",
   610  			)
   611  
   612  		}
   613  
   614  		for kk, vv := range tgt.KwArgs {
   615  			if len(vv) > 0 {
   616  				vals := cnv_values(vv)
   617  				str = append(str,
   618  					fmt.Sprintf(
   619  						"%s%s = [%s],",
   620  						indent,
   621  						kk,
   622  						w_py_strlist(vals),
   623  					),
   624  				)
   625  			}
   626  		}
   627  		str = append(str,
   628  			")",
   629  		)
   630  
   631  	}
   632  
   633  	// reindent:
   634  	for i, s := range str[1:] {
   635  		str[i+1] = indent + s
   636  	}
   637  
   638  	return strings.Join(str, "\n")
   639  }
   640  
   641  // EOF