flow(项目构建工具)(outdate)

2015年03月09日 08:12GMT+8

flow 目前作为 luxe 项目的构建, 源码是 nodejs 的。

作为项目构建工具它通过解析当前目录下的 project.flow 做如下事情:

  • icons - 嵌入和转换 icons 为所有目标(target)(除了 linux)

  • package - 快速打包 builds 为 zip 或 tar

  • clean - 删除 build 或项目输出, 或全部

  • upx - 针对桌面平台, 最终二进制文件可以自动压缩

  • files - 灵活的复制和模板系统

  • build - 将 haxe 代码生成部署应用

  • launch - 使用 flow 自带的 web server 加载, 以文件同步的方式(即修改了源码将立即反应)运行 web app,

安装

  1. haxelib install flow 即可

  2. 通过 git 克隆其源码, 然后以 haxelib dev flow DIR 的方式安装

由于 haxelib run flow 这个命令实在有些长, 因此你可能想要一个更短的命令。 对于 window 平台, 将文件保存为 bat,然后存放于 path 目录.

@echo off
haxelib run flow %*

Linux/Mac平台, 复制到文件保存到 /usr/local/bin 或 path 路径.

#!/bin/sh
haxelib run flow "$@"

基础命令

在命令行中输入 flow usage 来获取帮助信息. 可以详细到命令,如: flow usage run

command list :
    run, launch, build, compile, hooks, clean, package,
	setup, files, icons, config, upx, info, sync, usage

options:
  --version    : 与 flow version 一样
  --log<level> : 默值的 level 值为 2, 如果调试建议改为 3 或 4
  --json       : 输出 json 值仅用于特别的command及特别的usage选项

关于 usage 的帮助信息也可以查看 src/flow/cmd 目录下相应 .md 文件。

首先简单描术 build 和 compile 的区别:

  1. compile(编译)指将 haxe 源码转换成目标代码,它对应于 output 目录
  2. 而 build(构建)指使用各平台工具(例如: msvc)编译目标代码, 它对应于 output.build 目录

注: 一些命令将(command)依赖于 project.flow 配置文件

  • flow build [target] [--debug]: 编译并且构建, 生成 output 和 output.build

    • options: 任何 clean, hooks, files 命令的 options 都可用
    • –clean 在构建之前先执行 clean 命令
    • –output-path 覆盖 project.app.output 所指定的输出目录
  • flow compile [target] --options: 仅编译, 即仅生成 output

    • options:
    • –clean 在编译之前先执行 clean .
  • flow files [target] --options: “复制/模板处理”文件到 output 和 output.build,

    • options:
    • –no-build-files 将不会 “复制/模板处理” output.build
    • –no-files 将不会 “复制/模板处理” output
    • –no-list
    • –list-name
    • –error-on-missing
  • flow hooks [target] <--options>: 执行 project.flow 文件中 project.build.pre 项所定义的 nodejs 脚本

    // 示例来自: src/flow/cmd/hooks/hooks.js
    pre : {
        priority : 1,
        name : 'ios-project-sync',
        desc : 'checks the ios project project.app flags are synced up',
        script : 'pre/pre.js',
    }
    
  • flow icons [target] [--options]: 转换 project.app.icon 到 output

  • flow info [target] [--options]:解析config文件并返回指定平台的信息

    • options: –hxml 仅输出 hxml
  • flow launch [target] [--options]: 将启动最后成功构建的 app

    • options: 一些针对平台的特别选项, 自行参考 flow usage launch
    • –with-files 当仅更新 assets 和 config 时可以不需要编译,直接执行 launch 加上这个参数即可。
  • flow run [target] [--options] 将依次执行 files, build, launch

    • options: 所有依次执行命令的 options
  • flow package [target] [--options]: 打包 output 文件夹为压缩文件,默认为 zip

    • options:
    • --archive <tar, zip> 指定压缩格式
    • --archive-name <target/path/packagename> 指定打包后的路径及名称
    • --archive-root <subfolder> 指定压缩包的根目录, 默认情况下 output 下的文件将直接位于(archive)下
  • flow clean [target] --options: 清除指定目标文件夹,并且不会有任何警告

    • options:
    • –all 清除输出文件夹, 例如: (project)/bin 目录下文件将被完全删除
    • –clean-build 清除指定目标的输出文件夹下的 build 目录下所有文件
    • –clean-outout 清除指定目标的输出文件夹下所有文件
  • flow sync [target] --options 将执行 files 以及监视文件改变, options 见 files

  • flow config [leaf] [value] [--options] 只能设置或获得 config 已经定义了的 leaf,

    • 当给定 value 时, leaf 的值将被存储到 .flow.config.json 文件中去
    • 当没有指定 value 时, 仅输出 leaf 当前的 value,
    • options: –list 当 leaf 值为 Object 类型时,打印它的所有值

target 目前支持如下平台, 如果未指定则使用你的当前系统, 例如在 mac 中执行 flow build 则表现为 flow build mac --arch 64

// src/config.json
target : {
    "web"       : "js",
    "mac"       : "cpp",
    "windows"   : "cpp",
    "linux"     : "cpp",
    "android"   : "cpp",
    "ios"       : "cpp",
    "tvos"      : "cpp",
    "neko"      : "neko"
}

flow config

对于 flow 的配置文件, 分别有:

  • 原始 位于”flow/src/config.json”, 这个文件可以看下,在致了解下有些什么
  • 用户 对于 win7 很可能位于 “C:\Users\USERNAME.flow.config.json”, 如果没有需要自已新建。 比如使用这个文件用来配置 android 的 SDK 路径。
  • 项目 当前工作目录下的 “project.flow” 文件

并且这些配置从下往上覆盖, 通常对于一个项目来说 project.flow 是必须的. 一些命令依赖这个文件的配置

文件格式

https://snowkit.github.io/flow/flow.html flow 下使用如下约定:

  • node 指一个对象, 例如: {}

  • list 表示数组, 例:[]

  • key 为一对key/value ,例: name: 'value'

Root nodes

包含 3 个保留 nodes,分别为:

  • flow, 根据需要配置 flow 命令行本身. 配置细节参看 src/flow/config.json

    • 例如: 覆盖 build.android.sdk 或 build.android.ant_path 的路径设置
  • project, 项目的根节点, flow文件格式主要的部分

  • if, 同 project, 但允许条件检测.

custom root nodes

除了以上3个node, 整个flow文件可随意定制, flow文件处于于项目依赖树中. flow文件将被简单地处理, 并把这些值传递到 hooks,文件模板等等.

Project defines

在 haxe 代码常见的 defines 条件检测:

#if desktop
  desktop_code();
#else
  other_code();
#end

在flow文件中同样可以使用 defines 条件检测:

if:{
	desktop: {
		files: { desktop_config:'config/desktop.json => config.json' }
	}
}

Build in defines

内建的defines

  • dependency 依赖, 任何依赖将会成为 define,

    • 参考 haxelib,只是引用define时,使用下滑线”_“代替减号”-“
  • desktop 当编译为 mac,windows,或linux时

  • mobile: 当编译为 andoird或ios

  • target: 当前目标(target)将自动为 define

    • web,android,ios,mac,linux,windows….
  • arch-arch

    • on web, 将使用 arch-web

    • 如果 64位系统,则为 arch-64, 32位为 arch-32

    • arch-armv7, arch-i386 等等

  • ios-sim 当构建时指定了 --sim

  • debug: 当开启 --debug 时.

  • 通过 project.flow 文件定义的

Conditional project nodes

在根节点 if 中, 目前可以使用下边节点:

  • files

  • build.fiels

  • build.defines

  • build.flags

条件节点依赖于各种 defines

condition resolution

从 defines 中生成新的 define, 这样不必每次使用过长的条件表达式.

if : {
    "mac || windows || linux || android || ios" : {
      build: {
        defines : ['is_native']
      }
    },
    "is_native && arch-32" : {
        ...
    }
}

conditional statements

条件节点支持如下操作符:

  • ! not

  • || or

  • && and

但是注意: 使用条件操作符的表达式需要包括在引号内部(单或双引号)

A flow file

下边很多选项不是必须的使用默认值即可

{
  project : {

    /**
    项目的显示名称, 对于 mobile 目标,将显示为菜单标题

    @type String
    @required 表示为必须提供这个参数
    */
    name : 'empty',

    /**
    版本号, 和 haxelib 版本无关.使用 "major.minor.patch"

    @type String
    @required
    */
    version : '1.0.0',

    /**
    项目的作者/所属人/管理者, 可以为任意字母和数字的字符串

    @type String
    */
    author : 'luxeengine',

    // {}
    app : {

      /**
      应用的二进制文件名称,不能包括特殊字符(需要符合系统文件名称规范)

      @type String
      @default "flow_app"
      */
      name : 'flow_app',

      /**
      bundle/package/app的标识符, 不能包括特殊字符和空格. 你应该更改这个属性

      @type String
      @default "org.snowkit.flow_app"
      */
      package : 'org.snowkit.flow_app'

      /**
      指定输出文件夹名称

      @type String
      @default "bin"
      */
      output : 'bin/'


      /**
      指定haxe的主类,不需要写 ".hx" 扩展名

      @type String
      @default "Main"
      */
      main : 'Main',

      /**
      指定源码所在目录, 等同于 haxe 的 `-cp` 参数所指定的目录

      @type Array<String>
      @default "src"
      */
      codepaths: ['src'],

      /**
      指定 icons 的文件位置. (TODO:文档描述的不明确,只参看了 luxe项目的 luxe.flow 文件)

      当值仅为 path 时, 这时假定图标的名称为 "icon" , 相当于 path => icon

      当值为 path => name时, 将使用指定的 name 作为图标名称,

      path 将相对于 project.flow 的文件所在位置

      文件目录示例参考: `flow/src/flow/cmd/icons/default`

      @type String
      @default 将使用flow内部的图标集,即上边示例
      */
      icon: 'path/to/ => icon',


      // node, 很明显这个节点用于 html5形式的 app.
      web : {

        /**
        这些名称将传递到模板中去.

        libname: 给定库名称
        path: 相对于 output 指定的目录, 可以为绝对路径如 http://
        @type node of keys {libname: 'path'}
        */
        libs : {
          jquery: "js/jquery.js",
          index: "index.js"
        }
      },

      // node, 用于 mobile 目标
      mobile : {

          /**
          if device fullscreen is used.

          @type Bool
          @default true
          */
          fullsceen: true,

          /**
          横屏(landscape)竖屏(portrait)

          可用值为: "landscape","landscape left","landscape both","portrait","portrait upside down","portrait both"

          @type String
          @default "landscape"
          */
          orientation: "landscape",

          //node
          ios : {

            /**
            在 xcode 中指定设备类型

            有效值为: "Universal","iPhone","iPad"
            @type String
            @default "Universal"
            */
            devices: "Universal",

            /**
            指定 ios 的最低版本

            @type String
            @default "6.0"
            */
            deployment_target: "6.0",

            /**
            xcode 工具包名用于cpp, 不需要更改,除非框架需求, 允许框架开发者定制 cpp 库,

            @type String
            default "libc++"
            */
            cpp: "libc++"
            		  },

          //node
          android : {
            /**
            debug/store 编译标记,可用值为: "store","debug"

            @type String
            @default "debug"
            */
            build_type: "debug",

            /**
            installLocation in android manifest:
            http://developer.android.com/guide/topics/manifest/manifest-element.html#install

            @type String
            @default "preferExternal"
            */
            install_location: "preferExternal",

            /**
            sdk min in android manifest:
            http://developer.android.com/guide/topics/manifest/uses-sdk-element.html

            @type String
            @default "10"
            */
            sdk_min: "10",

            /**
            sdk target in android manifest:
            http://developer.android.com/guide/topics/manifest/uses-sdk-element.html

            通常设置为可用的最高.

            @type String
            @default "19"
            */
            sdk_target: "19",

            /**
            android manifest permissions:
            http://developer.android.com/reference/android/Manifest.permission.html

            @type Array<String>
            @default []
            */
            permissions: [
              'android.permission.READ_EXTERNAL_STORAGE',
              'android.permission.INTERNET'
            ]
          }
      }
    },

    build : {
      /**
      haxe 编译标记, 将直接传递给 haxe.exe

      @type Array<String>
      @default []
      */
      flags : ['-v', '--macro keep(example)'],


      /**
      自定义 haxe defines, 通过`-D define` 形式传递给 haxe.exe

      @type Array<String>
      @default []
      */
      defines : ['no_sfx', 'no_music'],

      /**
      key/value 形式的hash列表. 指定 haxelib
      key 应该为 haxelib list 中列出的库, version 为 "*" 时表示为当前版本, 或者指定成 "1.0.0" 的形式.

      被依赖的库将会优先读取 .flow 文件, 如果不存在则读取 haxelib.json 文件分析内部的依赖
      @type {}, i.e. {depend: "version"}
      @default {}
      */
      dependencies : {
        hxnodejs : '*'
      },

      /**
      位于 project/build下的files,将复制文件到 output/target.build/ 下, 其它参考 project/files 的说明

      i.e bin/web.build/

      web.build目录和web目录的区别是: web.build 是存放haxe源码的,而web目录则为最终生成结果的目录
      */
      files:{
      }
    },


    /**
    位于 project 下的 files,将复制文件到 output/target/ 例: bin/web/
    位于 project/build/下的files 则复制文件到 output/target.build/ 例: bin/web.build/

    基本格式为: {filereference: "path/source.ext => dest/path/dest.ext"}, 注意 "=>" 符号

    source: 源文件位置, 相对于 project.flow 文件
    dest: 目标位置, 相对于目标文件夹
      - 将被创建, 包括目录, 可以被覆盖重写(后边描述如何禁止覆盖)
      - 部分为可选, 这时将表现为 `name: "source => source"`

    @type {}
    @default {}
    */
    files : {
      data : 'data/ => assets/',
      // 带有 not_listed 参数
      howlerjs : { path:'flow/web/lib/howler.min.js => lib/howler.js', not_listed:true }
      /**
      如需通过模板生成, 则写成这种形式, 模板使用 [handlebars](http://handlebarsjs.com/)
      */
      config : {
          /**
          @type String
          */
          path: 'config.template.json => config.json',
          /**
          指定的值为 flow文件的根节点(除了if节点),这些节点和自定义节点可以作为模板参数传递
          @type Array<String>|String
          */
          template:['project']
      },
          // example
          boot : { path:'flow/boot/SnowApp.hx => haxe/SnowApp.hx', template:['project', 'snow'] }
    }
  },
  if : {
    ...
  }
}

misc

flow源码结构

bin/            # 包括各平台的 node.exe
setup/          # haxelib run flow 的快捷方式,
     /Flow.hx       # run.n 的源码, 获得 args 参数然后传递调用 node flow.js
    /flow/          # 目录下所有代码基于 nodejs
          /cmd/     # flow <command>,各命令, 这种方式真是简洁
          /node_modules/
          /project/	# flow.js 加载 project.js 由 project.js 加载这个目录下所有脚本
                  /dependencies/
                               /hxcpp.flow	# ?主要是移动hxcpp编译之后的库文件到 .build下
                  /prepare/     #
                          /conditions.js    #
                          /defines.js       #
                          /depends.js       #
                          /files.js         #
                          /flags.js         #
                  /bake.js          #
                  /prepare.js       #
                  /project.js       # 将被初使化加载到 flow.project 属性下
          /tools/       # http 服务器用于 web 调试
          /utils/
                /er.js          # 打印一朵字符串拼凑的花,
                /flagger.js     # 解析命令行参数到
                /haxelib.js     # 方便从 haxelib 获取一些信息
                /process.js     # 使用 child_process 运行 cmd,并从 stdout 获得结果
                /util.js        # 一些 Array,Object 方法
          flow.js       # 入口文件
          config.json	# 用于配置flow,可在个人项目中如 project.flow 的 flow 节点中覆盖这个文件的设置
                        # 例如覆盖 android sdk 的正确路径
          project.defaults.json     # project.flow 项目的默认配置, 被加载到 flow.project.default
src/
tests/      # 包含一个 flow.json 的配置样例