aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--doc/src/manual/cowboy_req.asciidoc793
-rw-r--r--doc/src/manual/cowboy_req.header.asciidoc74
-rw-r--r--doc/src/manual/cowboy_req.headers.asciidoc51
-rw-r--r--doc/src/manual/cowboy_req.host.asciidoc52
-rw-r--r--doc/src/manual/cowboy_req.method.asciidoc60
-rw-r--r--doc/src/manual/cowboy_req.path.asciidoc51
-rw-r--r--doc/src/manual/cowboy_req.peer.asciidoc61
-rw-r--r--doc/src/manual/cowboy_req.port.asciidoc52
-rw-r--r--doc/src/manual/cowboy_req.qs.asciidoc50
-rw-r--r--doc/src/manual/cowboy_req.scheme.asciidoc55
-rw-r--r--doc/src/manual/cowboy_req.uri.asciidoc116
-rw-r--r--doc/src/manual/cowboy_req.version.asciidoc49
-rw-r--r--ebin/cowboy.app5
-rw-r--r--erlang.mk829
-rw-r--r--rebar.config4
16 files changed, 1409 insertions, 895 deletions
diff --git a/Makefile b/Makefile
index 0c15e0f..f8f6bb6 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,8 @@ DEPS = cowlib ranch
dep_cowlib = git https://github.com/ninenines/cowlib master
dep_ranch = git https://github.com/ninenines/ranch 1.2.1
+DOC_DEPS = asciideck
+
TEST_DEPS = ct_helper gun
dep_ct_helper = git https://github.com/extend/ct_helper master
dep_gun = git https://github.com/ninenines/gun master
diff --git a/doc/src/manual/cowboy_req.asciidoc b/doc/src/manual/cowboy_req.asciidoc
index e1a2c4d..b1d97ff 100644
--- a/doc/src/manual/cowboy_req.asciidoc
+++ b/doc/src/manual/cowboy_req.asciidoc
@@ -6,685 +6,188 @@ cowboy_req - HTTP request and response
== Description
-The `cowboy_req` module provides functions to access, manipulate
+The module `cowboy_req` provides functions to access, manipulate
and respond to requests.
-The functions in this module follow patterns for their return types,
-based on the kind of function.
+There are four types of functions in this module. They can be
+differentiated by their name and their return type:
-* access: `Value`
-* action: `ok | {Result, Req} | {Result, Value, Req}`
-* modification: `Req`
-* question: `boolean()`
-
-Whenever `Req` is returned, you must use this returned value and
-ignore any previous you may have had. This value contains various
-values which are necessary for Cowboy to keep track of the request
-and response states.
-
-All functions which perform an action should only be called once.
-This includes reading the request body or replying. Cowboy will
-throw an error on the second call when it detects suspicious behavior.
+[options="header"]
+|===
+| Type | Name pattern | Return type
+| access | no verb, parse_*, match_* | `Value`
+| question | has_* | `boolean()`
+| modification | set_* | `Req`
+| action | any other verb | `ok \| {Result, Value, Req}`
+|===
-It is highly discouraged to pass the Req object to another process.
-Doing so and calling `cowboy_req` functions from it leads to
-undefined behavior.
+Any `Req` returned must be used in place of the one passed as
+argument. Functions that perform an action in particular write
+state in the Req object to make sure you are using the function
+correctly. For example, it's only possible to send one response,
+and to read the body once.
+
+== Exports
+
+Raw request:
+
+* link:man:cowboy_req:method(3)[cowboy_req:method(3)] - HTTP method
+* link:man:cowboy_req:version(3)[cowboy_req:version(3)] - HTTP version
+* link:man:cowboy_req:scheme(3)[cowboy_req:scheme(3)] - URI scheme
+* link:man:cowboy_req:host(3)[cowboy_req:host(3)] - URI host name
+* link:man:cowboy_req:port(3)[cowboy_req:port(3)] - URI port number
+* link:man:cowboy_req:path(3)[cowboy_req:path(3)] - URI path
+* link:man:cowboy_req:qs(3)[cowboy_req:qs(3)] - URI query string
+* link:man:cowboy_req:uri(3)[cowboy_req:uri(3)] - Reconstructed URI
+* link:man:cowboy_req:header(3)[cowboy_req:header(3)] - HTTP header
+* link:man:cowboy_req:headers(3)[cowboy_req:headers(3)] - HTTP headers
+* link:man:cowboy_req:peer(3)[cowboy_req:peer(3)] - Peer address and port
+
+Processed request:
+
+* link:man:cowboy_req:parse_qs(3)[cowboy_req:parse_qs(3)] - Parse the query string
+* link:man:cowboy_req:match_qs(3)[cowboy_req:match_qs(3)] - Match the query string against constraints
+* link:man:cowboy_req:parse_header(3)[cowboy_req:parse_header(3)] - Parse the given HTTP header
+* link:man:cowboy_req:parse_cookies(3)[cowboy_req:parse_cookies(3)] - Parse cookie headers
+* link:man:cowboy_req:match_cookies(3)[cowboy_req:match_cookies(3)] - Match cookies against constraints
+* link:man:cowboy_req:binding(3)[cowboy_req:binding(3)] - Access a value bound from the route
+* link:man:cowboy_req:bindings(3)[cowboy_req:bindings(3)] - Access all values bound from the route
+* link:man:cowboy_req:host_info(3)[cowboy_req:host_info(3)] - Access the route's heading host segments
+* link:man:cowboy_req:path_info(3)[cowboy_req:path_info(3)] - Access the route's trailing path segments
+
+Request body:
+
+* link:man:cowboy_req:has_body(3)[cowboy_req:has_body(3)] - Is there a request body?
+* link:man:cowboy_req:body_length(3)[cowboy_req:body_length(3)] - Body length
+* link:man:cowboy_req:read_body(3)[cowboy_req:read_body(3)] - Read the request body
+* link:man:cowboy_req:read_urlencoded_body(3)[cowboy_req:read_urlencoded_body(3)] - Read and parse a urlencoded request body
+* link:man:cowboy_req:read_part(3)[cowboy_req:read_part(3)] - Read the next part of a multipart body
+* link:man:cowboy_req:read_part_body(3)[cowboy_req:read_part_body(3)] - Read the current part's body in a multipart body
+
+Response:
+
+* link:man:cowboy_req:set_resp_cookie(3)[cowboy_req:set_resp_cookie(3)] - Set a cookie
+* link:man:cowboy_req:set_resp_header(3)[cowboy_req:set_resp_header(3)] - Set a response header
+* link:man:cowboy_req:has_resp_header(3)[cowboy_req:has_resp_header(3)] - Is the given response header set?
+* link:man:cowboy_req:delete_resp_header(3)[cowboy_req:delete_resp_header(3)] - Delete a response header
+* link:man:cowboy_req:set_resp_body(3)[cowboy_req:set_resp_body(3)] - Set the response body
+* link:man:cowboy_req:has_resp_body(3)[cowboy_req:has_resp_body(3)] - Is there a response body?
+* link:man:cowboy_req:reply(3)[cowboy_req:reply(3)] - Send the response
+* link:man:cowboy_req:stream_reply(3)[cowboy_req:stream_reply(3)] - Send the response and stream its body
+* link:man:cowboy_req:stream_body(3)[cowboy_req:stream_body(3)] - Send a chunk of the response body
+* link:man:cowboy_req:push(3)[cowboy_req:push(3)] - Push a resource to the client
== Types
-=== body_opts() = [Option]
+=== push_opts()
[source,erlang]
----
-Option = {continue, boolean()}
- | {length, non_neg_integer()}
- | {read_length, non_neg_integer()}
- | {read_timeout, timeout()}
- | {transfer_decode, transfer_decode_fun(), any()}
- | {content_decode, content_decode_fun()}
+push_opts() :: #{
+ method => binary(), %% case sensitive
+ scheme => binary(), %% lowercase; case insensitive
+ host => binary(), %% lowercase; case insensitive
+ port => inet:port_number(),
+ qs => binary() %% case sensitive
+}
----
-Request body reading options.
+Push options.
-=== cookie_opts() = [Option]
+By default, Cowboy will use the GET method, an empty query string,
+and take the scheme, host and port directly from the current
+request's URI.
+
+=== read_body_opts()
[source,erlang]
----
-Option = {max_age, non_neg_integer()}
- | {domain, binary()}
- | {path, binary()}
- | {secure, boolean()}
- | {http_only, boolean()}
+read_body_opts() :: #{
+ length => non_neg_integer(),
+ period => non_neg_integer(),
+ timeout => timeout()
+}
----
-Cookie options.
-
-=== req() - opaque to the user
-
-The Req object.
-
-All functions in this module receive a `Req` as argument,
-and some of them return a new object labelled `Req2` in
-the function descriptions below.
-
-== Request related exports
-
-=== binding(Name, Req) -> binding(Name, Req, undefined)
-
-Alias of `cowboy_req:binding/3`.
-
-=== binding(Name, Req, Default) -> Value
-
-Name = atom():: Binding name.
-Default = any():: Default value.
-Value = any() | Default:: Binding value.
-
-Return the value for the given binding.
-
-By default the value is a binary, however constraints may change
-the type of this value (for example automatically converting
-numbers to integer).
-
-=== bindings(Req) -> [{Name, Value}]
-
-Name = atom():: Binding name.
-Value = any():: Binding value.
-
-Return all bindings.
-
-By default the value is a binary, however constraints may change
-the type of this value (for example automatically converting
-numbers to integer).
-
-=== header(Name, Req) -> header(Name, Req, undefined)
-
-Alias of `cowboy_req:header/3`.
-
-=== header(Name, Req, Default) -> Value
-
-Name = binary():: Request header name.
-Default = any():: Default value.
-Value = binary() | Default:: Request header value.
-
-Return the value for the given header.
-
-While header names are case insensitive, this function expects
-the name to be a lowercase binary.
-
-=== headers(Req) -> Headers
-
-Headers = cowboy:http_headers():: Request headers.
-
-Return all headers.
-
-=== host(Req) -> Host
-
-Host = binary():: Requested host.
-
-Return the requested host.
-
-=== host_info(Req) -> HostInfo
-
-HostInfo = cowboy_router:tokens() | undefined:: Extra tokens for the host.
-
-Return the extra tokens from matching against `...` during routing.
-
-=== host_url(Req) -> HostURL
-
-HostURL = binary() | undefined:: Requested URL, without the path component.
-
-Return the requested URL excluding the path component.
-
-This function will always return `undefined` until the
-`cowboy_router` middleware has been executed.
-
-=== match_cookies(Fields, Req) -> Map
-
-Fields = cowboy:fields():: Cookie fields match rules.
-Map = map():: Cookie fields matched.
-
-Match cookies against the given fields.
-
-Cowboy will only return the cookie values specified in the
-fields list, and ignore all others. Fields can be either
-the name of the cookie requested; the name along with a
-list of constraints; or the name, a list of constraints
-and a default value in case the cookie is missing.
-
-This function will crash if the cookie is missing and no
-default value is provided. This function will also crash
-if a constraint fails.
-
-The name of the cookie must be provided as an atom. The
-key of the returned map will be that atom. The value may
-be converted through the use of constraints, making this
-function able to extract, validate and convert values all
-in one step.
-
-=== match_qs(Fields, Req) -> Map
-
-Fields = cowboy:fields():: Query string fields match rules.
-Map = map():: Query string fields matched.
-
-Match the query string against the given fields.
-
-Cowboy will only return the query string values specified
-in the fields list, and ignore all others. Fields can be
-either the key requested; the key along with a list of
-constraints; or the key, a list of constraints and a
-default value in case the key is missing.
-
-This function will crash if the key is missing and no
-default value is provided. This function will also crash
-if a constraint fails.
-
-The key must be provided as an atom. The key of the
-returned map will be that atom. The value may be converted
-through the use of constraints, making this function able
-to extract, validate and convert values all in one step.
-
-=== meta(Name, Req) -> meta(Name, Req, undefined)
-
-Alias for `cowboy_req:meta/3`.
-
-=== meta(Name, Req, Default) -> Value
-
-Name = atom():: Metadata name.
-Default = any():: Default value.
-Value = any():: Metadata value.
-
-Return metadata about the request.
-
-=== method(Req) -> Method
-
-Method = binary():: Request method.
-
-Return the method.
-
-Methods are case sensitive. Standard methods are always uppercase.
-
-=== parse_cookies(Req) -> [{Name, Value}]
-
-Name = binary():: Cookie name.
-Value = binary():: Cookie value.
-
-Parse and return all cookies.
-
-Cookie names are case sensitive.
-
-=== parse_header(Name, Req) -> see below
-
-Alias of `cowboy_req:parse_header/3`.
-
-The `parse_header/2` function will call `parser_header/3` with a
-different default value depending on the header being parsed. The
-following table summarizes the default values used.
-
-[cols="<,^",options="header"]
-|===
-| Header name | Header value
-| content-length | `0`
-| cookie | `[]`
-| transfer-encoding | `[<<"identity">>]`
-| Any other header | `undefined`
-|===
-
-=== parse_header(Name, Req, Default) -> ParsedValue | Default
-
-Name = binary():: Request header name.
-Default = any():: Default value.
-ParsedValue - see below:: Parsed request header value.
-
-Parse the given header.
-
-While header names are case insensitive, this function expects
-the name to be a lowercase binary.
-
-The parsed value differs depending on the header being parsed. The
-following table summarizes the different types returned.
-
-[cols="<,^",options="header"]
-|===
-| Header name | Type of parsed header value
-| accept | `[{{Type, SubType, Params}, Quality, AcceptExt}]`
-| accept-charset | `[{Charset, Quality}]`
-| accept-encoding | `[{Encoding, Quality}]`
-| accept-language | `[{LanguageTag, Quality}]`
-| authorization | `{AuthType, Credentials}`
-| content-length | `non_neg_integer()`
-| content-type | `{Type, SubType, ContentTypeParams}`
-| cookie | `[{binary(), binary()}]`
-| expect | `[Expect \| {Expect, ExpectValue, Params}]`
-| if-match | `'*' \| [{weak \| strong, OpaqueTag}]`
-| if-modified-since | `calendar:datetime()`
-| if-none-match | `'*' \| [{weak \| strong, OpaqueTag}]`
-| if-unmodified-since | `calendar:datetime()`
-| range | `{Unit, [Range]}`
-| sec-websocket-protocol | `[binary()]`
-| transfer-encoding | `[binary()]`
-| upgrade | `[binary()]`
-| x-forwarded-for | `[binary()]`
-|===
-
-Types for the above table:
-
-* Type = SubType = Charset = Encoding = LanguageTag = binary()
-* AuthType = Expect = OpaqueTag = Unit = binary()
-* Params = ContentTypeParams = [{binary(), binary()}]
-* Quality = 0..1000
-* AcceptExt = [{binary(), binary()} | binary()]
-* Credentials - see below
-* Range = {non_neg_integer(), non_neg_integer() | infinity} | neg_integer()
-
-The cookie names and values, the values of the sec-websocket-protocol
-and x-forwarded-for headers, the values in `AcceptExt` and `Params`,
-the authorization `Credentials`, the `ExpectValue` and `OpaqueTag`
-are case sensitive. All values in `ContentTypeParams` are case sensitive
-except the value of the charset parameter, which is case insensitive.
-All other values are case insensitive and will be returned as lowercase.
-
-The headers accept, accept-encoding and cookie headers can return
-an empty list. Some other headers are expected to have a value if provided
-and may crash if the value is missing.
-
-The authorization header parsing code currently only supports basic
-HTTP authentication. The `Credentials` type is thus `{Username, Password}`
-with `Username` and `Password` being `binary()`.
-
-The range header value `Range` can take three forms:
-
-* `{From, To}`: from `From` to `To` units
-* `{From, infinity}`: everything after `From` units
-* `-Final`: the final `Final` units
-
-An `undefined` tuple will be returned if Cowboy doesn't know how
-to parse the requested header.
-
-=== parse_qs(Req) -> [{Name, Value}]
-
-Name = binary():: Query string field name.
-Value = binary() | true:: Query string field value.
-
-Return the request's query string as a list of tuples.
-
-The atom `true` is returned for keys which have no value.
-Keys with no value are different from keys with an empty
-value in that they do not have a `=` indicating the presence
-of a value.
-
-=== path(Req) -> Path
-
-Path = binary():: Requested path.
-
-Return the requested path.
-
-=== path_info(Req) -> PathInfo
-
-PathInfo = cowboy_router:tokens() | undefined:: Extra tokens for the path.
-
-Return the extra tokens from matching against `...` during routing.
-
-=== peer(Req) -> Peer
-
-Peer = `{inet:ip_address(), inet:port_number()}`:: Peer IP address and port number.
-
-Return the client's IP address and port number.
-
-=== port(Req) -> Port
-
-Port = inet:port_number():: Requested port number.
-
-Return the request's port.
-
-The port returned by this function is obtained by parsing
-the host header. It may be different than the actual port
-the client used to connect to the Cowboy server.
-
-=== qs(Req) -> QueryString
-
-QueryString = binary():: Unprocessed query string.
-
-Return the request's query string.
-
-=== set_meta(Name, Value, Req) -> Req2
-
-Name = atom():: Metadata name.
-Value = any():: Metadata value.
-
-Set metadata about the request.
-
-An existing value will be overwritten.
-
-=== url(Req) -> URL
-
-URL = binary() | undefined:: Requested URL.
+Body reading options.
-Return the requested URL.
+The defaults are function-specific.
-This function will always return `undefined` until the
-`cowboy_router` middleware has been executed.
+=== req()
-=== version(Req) -> Version
-
-Version = cowboy:http_version():: Client's advertised HTTP version.
-
-Return the HTTP version used for this request.
-
-== Request body related exports
-
-=== body(Req) -> body(Req, [])
-
-Alias of `cowboy_req:body/2`.
-
-=== body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2}
-
-Opts = [body_opt()]:: Request body reading options.
-Data = binary():: Data read from the body.
-
-Read the request body.
-
-This function will read a chunk of the request body. If there is
-more data to be read after this function call, then a `more` tuple
-is returned. Otherwise an `ok` tuple is returned.
-
-Cowboy will automatically send a `100 Continue` reply if
-required. If this behavior is not desirable, it can be disabled
-by setting the `continue` option to `false`.
-
-Cowboy will by default attempt to read up to 8MB of the body,
-but in chunks of 1MB. It will use a timeout of 15s per chunk.
-All these values can be changed using the `length`, `read_length`
-and `read_timeout` options respectively. Note that the size
-of the data may not be the same as requested as the decoding
-functions may grow or shrink it, and Cowboy makes not attempt
-at returning an exact amount.
-
-Cowboy will properly handle chunked transfer-encoding by
-default. If any other transfer-encoding or content-encoding
-has been used for the request, custom decoding functions
-can be used. The `content_decode` and `transfer_decode`
-options allow setting the decode functions manually.
-
-After the body has been streamed fully, Cowboy will remove
-the transfer-encoding header from the Req object, and add
-the content-length header if it wasn't already there.
-
-This function can only be called once. Cowboy will not cache
-the result of this call.
-
-=== body_length(Req) -> Length
-
-Length = non_neg_integer() | undefined:: Length of the request body.
-
-Return the length of the request body.
-
-The length will only be returned if the request does not
-use any transfer-encoding and if the content-length header
-is present.
-
-=== body_qs(Req) -> body_qs(Req, [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}])
-
-Alias of `cowboy_req:body_qs/2`.
-
-=== body_qs(Req, Opts) -> {ok, [{Name, Value}], Req2} | {badlength, Req2}
-
-Opts = [body_opt()]:: Request body reading options.
-Name = binary():: Field name.
-Value = binary() | true:: Field value.
-
-Return the request body as a list of tuples.
-
-This function will parse the body assuming the content-type
-application/x-www-form-urlencoded, commonly used for the
-query string.
-
-This function calls `body/2` for reading the body, with the
-same options it received. By default it will attempt to read
-a body of 64KB in one chunk, with a timeout of 5s. If the
-body is larger then a `badlength` tuple is returned.
-
-This function can only be called once. Cowboy will not cache
-the result of this call.
-
-=== has_body(Req) -> boolean()
-
-Return whether the request has a body.
-
-=== part(Req) -> part(Req, [{length, 64000}, {read_length, 64000}, {read_timeout, 5000}])
-
-Alias of `cowboy_req:part/2`.
-
-=== part(Req, Opts) -> {ok, Headers, Req2} | {done, Req2}
-
-Opts = [body_opt()]:: Request body reading options.
-Headers = cow_multipart:headers():: Part's headers.
-
-Read the headers for the next part of the multipart message.
-
-Cowboy will skip any data remaining until the beginning of
-the next part. This includes the preamble to the multipart
-message but also the body of a previous part if it hasn't
-been read. Both are skipped automatically when calling this
-function.
-
-The headers returned are MIME headers, NOT HTTP headers.
-They can be parsed using the functions from the `cow_multipart`
-module. In addition, the `cow_multipart:form_data/1` function
-can be used to quickly figure out `multipart/form-data` messages.
-It takes the list of headers and returns whether this part is
-a simple form field or a file being uploaded.
-
-Note that once a part has been read, or skipped, it cannot
-be read again.
-
-This function calls `body/2` for reading the body, with the
-same options it received. By default it will only read chunks
-of 64KB with a timeout of 5s. This is tailored for reading
-part headers, not for skipping the previous part's body.
-You might want to consider skipping large parts manually.
-
-=== part_body(Req) -> part_body(Req, [])
-
-Alias of `cowboy_req:part_body/2`.
-
-=== part_body(Req, Opts) -> {ok, Data, Req2} | {more, Data, Req2}
-
-Opts = [body_opt()]:: Request body reading options.
-Data = binary():: Part's body.
-
-Read the body of the current part of the multipart message.
-
-This function calls `body/2` for reading the body, with the
-same options it received. It uses the same defaults.
-
-If there are more data to be read from the socket for this
-part, the function will return what it could read inside a
-`more` tuple. Otherwise, it will return an `ok` tuple.
-
-Calling this function again after receiving a `more` tuple
-will return another chunk of body. The last chunk will be
-returned inside an `ok` tuple.
-
-Note that once the body has been read, fully or partially,
-it cannot be read again.
-
-== Response related exports
-
-=== chunk(Data, Req) -> ok
-
-Data = iodata():: Chunk data to be sent.
-
-Send a chunk of data.
-
-This function should be called as many times as needed
-to send data chunks after calling `chunked_reply/{2,3}`.
-
-When the method is HEAD, no data will actually be sent.
-
-If the request uses HTTP/1.0, the data is sent directly
-without wrapping it in an HTTP/1.1 chunk, providing
-compatibility with older clients.
-
-=== chunked_reply(StatusCode, Req) -> chunked_reply(StatusCode, [], Req)
-
-Alias of `cowboy_req:chunked_reply/3`.
-
-=== chunked_reply(StatusCode, Headers, Req) -> Req2
-
-StatusCode = cowboy:http_status():: Response status code.
-Headers = cowboy:http_headers():: Response headers.
-
-Send a response using chunked transfer-encoding.
-
-This function effectively sends the response status line
-and headers to the client.
-
-This function will not send any body set previously. After
-this call the handler must use the `chunk/2` function
-repeatedly to send the body in as many chunks as needed.
-
-If the request uses HTTP/1.0, the data is sent directly
-without wrapping it in an HTTP/1.1 chunk, providing
-compatibility with older clients.
-
-This function can only be called once, with the exception
-of overriding the response in the `onresponse` hook.
-
-=== continue(Req) -> ok
-
-Send a 100 Continue intermediate reply.
-
-This reply is required before the client starts sending the
-body when the request contains the `expect` header with the
-`100-continue` value.
-
-Cowboy will send this automatically when required. However
-you may want to do it manually by disabling this behavior
-with the `continue` body option and then calling this
-function.
-
-=== delete_resp_header(Name, Req) -> Req2
-
-Name = binary():: Response header name.
-
-Delete the given response header.
-
-While header names are case insensitive, this function expects
-the name to be a lowercase binary.
-
-=== has_resp_body(Req) -> boolean()
-
-Return whether a response body has been set.
-
-This function will return false if a response body has
-been set with a length of 0.
-
-=== has_resp_header(Name, Req) -> boolean()
-
-Name = binary():: Response header name.
-
-Return whether the given response header has been set.
-
-While header names are case insensitive, this function expects
-the name to be a lowercase binary.
-
-=== reply(StatusCode, Req) -> reply(StatusCode, [], Req)
-
-Alias of `cowboy_req:reply/3`.
-
-=== reply(StatusCode, Headers, Req) - see below
-
-Alias of `cowboy_req:reply/4`, with caveats.
-
-=== reply(StatusCode, Headers, Body, Req) -> Req2
-
-StatusCode = cowboy:http_status():: Response status code.
-Headers = cowboy:http_headers():: Response headers.
-Body = iodata():: Response body.
-
-Send a response.
-
-This function effectively sends the response status line,
-headers and body to the client, in a single send function
-call.
-
-The `reply/2` and `reply/3` functions will send the body
-set previously, if any. The `reply/4` function overrides
-any body set previously and sends `Body` instead.
-
-If a body function was set, and `reply/2` or `reply/3` was
-used, it will be called before returning.
-
-No more data can be sent to the client after this function
-returns.
-
-This function can only be called once, with the exception
-of overriding the response in the `onresponse` hook.
-
-=== set_resp_body(Body, Req) -> Req2
-
-Body = iodata():: Response body.
-
-Set a response body.
-
-This body will not be sent if `chunked_reply/{2,3}` or
-`reply/4` is used, as they override it.
-
-=== set_resp_body_fun(Fun, Req) -> Req2
-
-Alias of `cowboy_req:set_resp_body_fun/3`.
-
-=== set_resp_body_fun(Length, Fun, Req) -> Req2
-
-Fun = fun((Socket, Transport) -> ok):: Fun that will send the response body.
-Socket = inet:socket():: Socket for this connection.
-Transport = module():: Transport module for this socket.
-Length = non_neg_integer():: Length of the response body.
-
-Set a fun for sending the response body.
+[source,erlang]
+----
+req() :: #{
+ method := binary(), %% case sensitive
+ version := cowboy:http_version() | atom(),
+ scheme := binary(), %% lowercase; case insensitive
+ host := binary(), %% lowercase; case insensitive
+ port := inet:port_number(),
+ path := binary(), %% case sensitive
+ qs := binary(), %% case sensitive
+ headers := cowboy:http_headers(),
+ peer := {inet:ip_address(), inet:port_number()}
+}
+----
-If a `Length` is provided, it will be sent in the
-content-length header in the response. It is recommended
-to set the length if it can be known in advance. Otherwise,
-the transfer-encoding header will be set to identity.
+The Req object.
-This function will only be called if the response is sent
-using the `reply/2` or `reply/3` function.
+Contains information about the request and response. While
+some fields are publicly documented, others aren't and shouldn't
+be used.
-The fun will receive the Ranch `Socket` and `Transport` as
-arguments. Only send and sendfile operations are supported.
+You may add custom fields if required. Make sure to namespace
+them by prepending an underscore and the name of your application:
-=== set_resp_body_fun(chunked, Fun, Req) -> Req2
+.Setting a custom field
+[source,erlang]
+----
+Req#{_myapp_auth_method => pubkey}.
+----
-Fun = fun((ChunkFun) -> ok):: Fun that will send the response body.
-ChunkFun = fun((iodata()) -> ok):: Fun to call for every chunk to be sent.
+=== resp_body()
-Set a fun for sending the response body using chunked transfer-encoding.
+[source,erlang]
+----
+resp_body() :: iodata()
+ | {sendfile, Offset, Length, Filename}
-This function will only be called if the response is sent
-using the `reply/2` or `reply/3` function.
+Offset :: non_neg_integer()
+Length :: pos_integer()
+Filename :: file:name_all()
+----
-The fun will receive another fun as argument. This fun is to
-be used to send chunks in a similar way to the `chunk/2` function,
-except the fun only takes one argument, the data to be sent in
-the chunk.
+Response body.
-=== set_resp_cookie(Name, Value, Opts, Req) -> Req2
+It can take two forms: the actual data to be sent or a
+tuple indicating which file to send.
-Name = iodata():: Cookie name.
-Value = iodata():: Cookie value.
-Opts = cookie_opts():: Cookie options.
+When sending data directly, the type is either a binary or
+an iolist. Iolists are an efficient way to build output.
+Instead of concatenating strings or binaries, you can simply
+build a list containing the fragments you want to send in the
+order they should be sent:
-Set a cookie in the response.
+.Example iolists usage
+[source,erlang]
+----
+1> RespBody = ["Hello ", [<<"world">>, $!]].
+["Hello ",[<<"world">>,33]]
+2> io:format("~s~n", [RespBody]).
+Hello world!
+----
-Cookie names are case sensitive.
+When using the sendfile tuple, the `Length` value is mandatory
+and must be higher than 0. It is sent with the response in the
+content-length header.
-=== set_resp_header(Name, Value, Req) -> Req2
+// @todo Make sure we have a test with an empty file...
+// @todo cowboy_static should probably NOT return a sendfile tuple if size is 0.
-Name = binary():: Response header name.
-Value = iodata():: Response header value.
+//%% While sendfile allows a Len of 0 that means "everything past Offset",
+//%% Cowboy expects the real length as it is used as metadata.
+//%% @todo We should probably explicitly reject it.
-Set a response header.
+== See also
-You should use `set_resp_cookie/4` instead of this function
-to set cookies.
+link:man:cowboy(7)[cowboy(7)]
diff --git a/doc/src/manual/cowboy_req.header.asciidoc b/doc/src/manual/cowboy_req.header.asciidoc
new file mode 100644
index 0000000..e16b902
--- /dev/null
+++ b/doc/src/manual/cowboy_req.header.asciidoc
@@ -0,0 +1,74 @@
+= cowboy_req:header(3)
+
+== Name
+
+cowboy_req:header - HTTP header
+
+== Description
+
+[source,erlang]
+----
+header(Name :: binary(), Req) -> header(Name, Req, undefined)
+header(Name :: binary(), Req, Default) -> binary() | Default
+
+Req :: cowboy_req:req()
+----
+
+Return the value for the given HTTP header.
+
+The header name must be given as a lowercase binary string.
+While header names are case insensitive, Cowboy requires them
+to be given as lowercase to function properly.
+
+Headers can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{headers := #{Name := Value}} = Req.
+----
+
+Note that this snippet will crash if the header is missing.
+
+== Arguments
+
+Name::
+
+Desired HTTP header name as a binary string.
+
+Req::
+
+The Req object.
+
+Default::
+
+Default value returned when the header is missing.
+
+== Return value
+
+The header value is returned as a binary string. When the
+header is missing, the default argument is returned.
+
+== Changelog
+
+* *2.0*: Only the header value is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the accept header
+[source,erlang]
+----
+Accept = cowboy_req:header(<<"accept">>, Req).
+----
+
+.Get the content-length header with a default value
+[source,erlang]
+----
+Length = cowboy_req:header(<<"content-length">>, Req, <<"0">>).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:headers(3)[cowboy_req:headers(3)],
+link:man:cowboy_req:parse_header(3)[cowboy_req:parse_header(3)]
diff --git a/doc/src/manual/cowboy_req.headers.asciidoc b/doc/src/manual/cowboy_req.headers.asciidoc
new file mode 100644
index 0000000..c370f6d
--- /dev/null
+++ b/doc/src/manual/cowboy_req.headers.asciidoc
@@ -0,0 +1,51 @@
+= cowboy_req:headers(3)
+
+== Name
+
+cowboy_req:headers - HTTP headers
+
+== Description
+
+[source,erlang]
+----
+headers(Req :: cowboy_req:req()) -> cowboy:http_headers()
+----
+
+Return all request headers.
+
+Request headers can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{headers := Headers} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+Headers are returned as a map with keys being lowercase
+binary strings, and values as binary strings.
+
+== Changelog
+
+* *2.0*: Only the headers are returned, they are no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get all headers
+[source,erlang]
+----
+Headers = cowboy_req:headers(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:header(3)[cowboy_req:header(3)],
+link:man:cowboy_req:parse_header(3)[cowboy_req:parse_header(3)]
diff --git a/doc/src/manual/cowboy_req.host.asciidoc b/doc/src/manual/cowboy_req.host.asciidoc
new file mode 100644
index 0000000..2c512e2
--- /dev/null
+++ b/doc/src/manual/cowboy_req.host.asciidoc
@@ -0,0 +1,52 @@
+= cowboy_req:host(3)
+
+== Name
+
+cowboy_req:host - URI host name
+
+== Description
+
+[source,erlang]
+----
+host(Req :: cowboy_req:req()) -> Host :: binary()
+----
+
+Return the host name of the effective request URI.
+
+The host name can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{host := Host} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The host name is returned as a lowercase binary string.
+It is case insensitive.
+
+== Changelog
+
+* *2.0*: Only the host name is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the effective request URI's host name
+[source,erlang]
+----
+Host = cowboy_req:host(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:binding(3)[cowboy_req:binding(3)],
+link:man:cowboy_req:bindings(3)[cowboy_req:bindings(3)],
+link:man:cowboy_req:host_info(3)[cowboy_req:host_info(3)]
diff --git a/doc/src/manual/cowboy_req.method.asciidoc b/doc/src/manual/cowboy_req.method.asciidoc
new file mode 100644
index 0000000..bb3ad6e
--- /dev/null
+++ b/doc/src/manual/cowboy_req.method.asciidoc
@@ -0,0 +1,60 @@
+= cowboy_req:method(3)
+
+== Name
+
+cowboy_req:method - HTTP method
+
+== Description
+
+[source,erlang]
+----
+method(Req :: cowboy_req:req()) -> Method :: binary()
+----
+
+Return the request's HTTP method.
+
+The method can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{method := Method} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The request's HTTP method is returned as a binary string.
+While methods are case sensitive, standard methods are
+always uppercase.
+
+== Changelog
+
+* *2.0*: Only the method is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Ensure the request's method is GET
+[source,erlang]
+----
+<<"GET">> = cowboy_req:method(Req).
+----
+
+.Allow methods from list
+[source,erlang]
+----
+init(Req, State) ->
+ case lists:member(cowboy_req:method(Req), [<<"GET">>, <<"POST">>]) of
+ true -> handle(Req, State);
+ false -> method_not_allowed(Req, State)
+ end.
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)]
diff --git a/doc/src/manual/cowboy_req.path.asciidoc b/doc/src/manual/cowboy_req.path.asciidoc
new file mode 100644
index 0000000..5f26548
--- /dev/null
+++ b/doc/src/manual/cowboy_req.path.asciidoc
@@ -0,0 +1,51 @@
+= cowboy_req:path(3)
+
+== Name
+
+cowboy_req:path - URI path
+
+== Description
+
+[source,erlang]
+----
+path(Req :: cowboy_req:req()) -> Path :: binary()
+----
+
+Return the path of the effective request URI.
+
+The path can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{path := Path} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The path is returned as a binary string. It is case sensitive.
+
+== Changelog
+
+* *2.0*: Only the path is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the effective request URI's path
+[source,erlang]
+----
+Path = cowboy_req:path(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:binding(3)[cowboy_req:binding(3)],
+link:man:cowboy_req:bindings(3)[cowboy_req:bindings(3)],
+link:man:cowboy_req:path_info(3)[cowboy_req:path_info(3)]
diff --git a/doc/src/manual/cowboy_req.peer.asciidoc b/doc/src/manual/cowboy_req.peer.asciidoc
new file mode 100644
index 0000000..e2df691
--- /dev/null
+++ b/doc/src/manual/cowboy_req.peer.asciidoc
@@ -0,0 +1,61 @@
+= cowboy_req:peer(3)
+
+== Name
+
+cowboy_req:peer - Peer address and port
+
+== Description
+
+[source,erlang]
+----
+peer(Req :: cowboy_req:req()) -> Peer
+
+Peer :: {inet:ip_address(), inet:port_number()}
+----
+
+Return the peer's IP address and port number.
+
+The peer can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{peer := {IP, Port}} = Req.
+----
+
+// @todo So we need tests for accessing the Req directly.
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The peer's IP address and port number.
+
+The peer is not necessarily the client's IP address and port.
+It is the IP address of the endpoint connecting directly to
+the server, which may be a gateway or a proxy.
+
+The forwarded header can be used to get better information
+about the different endpoints from the client to the server.
+Note however that it is only informative; there is no reliable
+way of determining the source of an HTTP request.
+
+== Changelog
+
+* *2.0*: Only the peer is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the peer IP address and port number.
+[source,erlang]
+----
+{IP, Port} = cowboy_req:peer(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)]
diff --git a/doc/src/manual/cowboy_req.port.asciidoc b/doc/src/manual/cowboy_req.port.asciidoc
new file mode 100644
index 0000000..57f0875
--- /dev/null
+++ b/doc/src/manual/cowboy_req.port.asciidoc
@@ -0,0 +1,52 @@
+= cowboy_req:port(3)
+
+== Name
+
+cowboy_req:port - URI port number
+
+== Description
+
+[source,erlang]
+----
+port(Req :: cowboy_req:req()) -> Port :: inet:port_number()
+----
+
+Return the port number of the effective request URI.
+
+Note that the port number returned by this function is obtained
+by parsing the host header. It may be different from the port
+the peer used to connect to Cowboy.
+
+The port number can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{port := Port} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The port number is returned as an integer.
+
+== Changelog
+
+* *2.0*: Only the port number is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the effective request URI's port number
+[source,erlang]
+----
+Port = cowboy_req:port(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)]
diff --git a/doc/src/manual/cowboy_req.qs.asciidoc b/doc/src/manual/cowboy_req.qs.asciidoc
new file mode 100644
index 0000000..6e92357
--- /dev/null
+++ b/doc/src/manual/cowboy_req.qs.asciidoc
@@ -0,0 +1,50 @@
+= cowboy_req:qs(3)
+
+== Name
+
+cowboy_req:qs - URI query string
+
+== Description
+
+[source,erlang]
+----
+qs(Req :: cowboy_req:req()) -> Qs :: binary()
+----
+
+Return the query string of the effective request URI.
+
+The query string can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{qs := Qs} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The query string is returned as a binary string. It is case sensitive.
+
+== Changelog
+
+* *2.0*: Only the query string is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the effective request URI's query string
+[source,erlang]
+----
+Qs = cowboy_req:qs(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:parse_qs(3)[cowboy_req:parse_qs(3)],
+link:man:cowboy_req:match_qs(3)[cowboy_req:match_qs(3)]
diff --git a/doc/src/manual/cowboy_req.scheme.asciidoc b/doc/src/manual/cowboy_req.scheme.asciidoc
new file mode 100644
index 0000000..bbe147d
--- /dev/null
+++ b/doc/src/manual/cowboy_req.scheme.asciidoc
@@ -0,0 +1,55 @@
+= cowboy_req:scheme(3)
+
+== Name
+
+cowboy_req:scheme - URI scheme
+
+== Description
+
+[source,erlang]
+----
+scheme(Req :: cowboy_req:req()) -> Scheme :: binary()
+----
+
+Return the scheme of the effective request URI.
+
+The scheme can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{scheme := Scheme} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The scheme is returned as a binary. It is case insensitive.
+
+Cowboy will only set the scheme to `<<"http">>` or `<<"https">>`.
+
+== Changelog
+
+* *2.0*: Function introduced.
+
+== Examples
+
+.Redirect HTTP to HTTPS
+[source,erlang]
+----
+init(Req0=#{scheme := <<"http">>}, State) ->
+ Req = cowboy_req:reply(302, #{
+ <<"location">> => cowboy_req:uri(Req, #{scheme => <<"https">>})
+ }, Req0),
+ {ok, Req, State};
+init(Req, State) ->
+ {cowboy_rest, Req, State}.
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)]
diff --git a/doc/src/manual/cowboy_req.uri.asciidoc b/doc/src/manual/cowboy_req.uri.asciidoc
new file mode 100644
index 0000000..790f863
--- /dev/null
+++ b/doc/src/manual/cowboy_req.uri.asciidoc
@@ -0,0 +1,116 @@
+= cowboy_req:uri(3)
+
+== Name
+
+cowboy_req:uri - Reconstructed URI
+
+== Description
+
+[source,erlang]
+----
+uri(Req :: cowboy_req:req()) -> uri(Req, #{})
+uri(Req :: cowboy_req:req(), Opts) -> URI :: iodata()
+
+Opts :: #{
+ scheme => iodata() | undefined,
+ host => iodata() | undefined,
+ port => inet:port_number() | undefined,
+ path => iodata() | undefined,
+ qs => iodata() | undefined,
+ fragment => iodata() | undefined
+}
+----
+
+Reconstruct the effective request URI, optionally modifying components.
+
+By default Cowboy will build a URI using the components found
+in the request. Options allow disabling or replacing individual
+components.
+
+== Arguments
+
+Req::
+
+The Req object.
+
+Opts::
+
+Map for overriding individual components.
++
+To replace a component, provide its new value as a binary
+string or an iolist. To disable a component, set its value
+to `undefined`.
++
+As this function always returns a valid URI, there are some
+things to note:
++
+ * Disabling the host also disables the scheme and port.
+ * There is no fragment component by default as these are
+ not sent with the request.
+ * The port number may not appear in the resulting URI if
+ it is the default port for the given scheme (http: 80; https: 443).
+
+== Return value
+
+The reconstructed URI is returned as an iolist or a binary string.
+
+== Changelog
+
+* *2.0*: Individual components can be replaced or disabled.
+* *2.0*: Only the URI is returned, it is no longer wrapped in a tuple.
+* *2.0*: Function introduced. Replaces `host_url/1` and `url/1`.
+
+== Examples
+
+With an effective request URI http://example.org/path/to/res?edit=1
+we can have:
+
+.Protocol relative form
+[source,erlang]
+----
+%% //example.org/path/to/res?edit=1
+cowboy_req:uri(Req, #{scheme => undefined}).
+----
+
+.Serialized origin for use in the origin header
+[source,erlang]
+----
+%% http://example.org
+cowboy_req:uri(Req, #{path => undefined, qs => undefined}).
+----
+
+.HTTP/1.1 origin form (path and query string only)
+[source,erlang]
+----
+%% /path/to/res?edit=1
+cowboy_req:uri(Req, #{host => undefined}).
+----
+
+.Add a fragment to the URI
+[source,erlang]
+----
+%% http://example.org/path/to/res?edit=1#errors
+cowboy_req:uri(Req, #{fragment => <<"errors">>}).
+----
+
+.Ensure the scheme is HTTPS
+[source,erlang]
+----
+%% https://example.org/path/to/res?edit=1
+cowboy_req:uri(Req, #{scheme => <<"https">>}).
+----
+
+.Convert the URI to a binary string
+[source,erlang]
+----
+iolist_to_binary(cowboy_req:uri(Req)).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)],
+link:man:cowboy_req:scheme(3)[cowboy_req:scheme(3)],
+link:man:cowboy_req:host(3)[cowboy_req:host(3)],
+link:man:cowboy_req:port(3)[cowboy_req:port(3)],
+link:man:cowboy_req:path(3)[cowboy_req:path(3)],
+link:man:cowboy_req:qs(3)[cowboy_req:qs(3)]
diff --git a/doc/src/manual/cowboy_req.version.asciidoc b/doc/src/manual/cowboy_req.version.asciidoc
new file mode 100644
index 0000000..15920c4
--- /dev/null
+++ b/doc/src/manual/cowboy_req.version.asciidoc
@@ -0,0 +1,49 @@
+= cowboy_req:version(3)
+
+== Name
+
+cowboy_req:version - HTTP version
+
+== Description
+
+[source,erlang]
+----
+version(Req :: cowboy_req:req()) -> Version :: cowboy:http_version()
+----
+
+Return the HTTP version used for the request.
+
+The version can also be obtained using pattern matching:
+
+[source,erlang]
+----
+#{version := Version} = Req.
+----
+
+== Arguments
+
+Req::
+
+The Req object.
+
+== Return value
+
+The HTTP version used for the request is returned as an
+atom. It is provided for informative purposes only.
+
+== Changelog
+
+* *2.0*: Only the version is returned, it is no longer wrapped in a tuple.
+* *1.0*: Function introduced.
+
+== Examples
+
+.Get the HTTP version
+[source,erlang]
+----
+Version = cowboy_req:version(Req).
+----
+
+== See also
+
+link:man:cowboy_req(3)[cowboy_req(3)]
diff --git a/ebin/cowboy.app b/ebin/cowboy.app
index f44c8fc..f21a3cf 100644
--- a/ebin/cowboy.app
+++ b/ebin/cowboy.app
@@ -1,8 +1,9 @@
{application, cowboy, [
{description, "Small, fast, modern HTTP server."},
- {vsn, "2.0.0-pre.2"},
+ {vsn, "2.0.0-pre.4"},
{modules, ['cowboy','cowboy_app','cowboy_bstr','cowboy_clear','cowboy_clock','cowboy_constraints','cowboy_handler','cowboy_http','cowboy_http2','cowboy_loop','cowboy_middleware','cowboy_req','cowboy_rest','cowboy_router','cowboy_static','cowboy_stream','cowboy_stream_h','cowboy_sub_protocol','cowboy_sup','cowboy_tls','cowboy_websocket']},
{registered, [cowboy_sup,cowboy_clock]},
{applications, [kernel,stdlib,crypto,cowlib,ranch]},
- {mod, {cowboy_app, []}}
+ {mod, {cowboy_app, []}},
+ {env, []}
]}. \ No newline at end of file
diff --git a/erlang.mk b/erlang.mk
index 3ad1a48..38bea99 100644
--- a/erlang.mk
+++ b/erlang.mk
@@ -1,4 +1,4 @@
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
@@ -12,11 +12,21 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.PHONY: all app deps search rel docs install-docs check tests clean distclean help erlang-mk
+.PHONY: all app apps deps search rel relup docs install-docs check tests clean distclean help erlang-mk
ERLANG_MK_FILENAME := $(realpath $(lastword $(MAKEFILE_LIST)))
-ERLANG_MK_VERSION = 2.0.0-pre.2-75-g18a7074-dirty
+ERLANG_MK_VERSION = 2016.11.03-4-g9e9b7d2
+
+# Make 3.81 and 3.82 are deprecated.
+
+ifeq ($(MAKE_VERSION),3.81)
+$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
+endif
+
+ifeq ($(MAKE_VERSION),3.82)
+$(warning Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html)
+endif
# Core configuration.
@@ -24,6 +34,8 @@ PROJECT ?= $(notdir $(CURDIR))
PROJECT := $(strip $(PROJECT))
PROJECT_VERSION ?= rolling
+PROJECT_MOD ?= $(PROJECT)_app
+PROJECT_ENV ?= []
# Verbosity.
@@ -84,6 +96,8 @@ all:: deps app rel
rel::
$(verbose) :
+relup:: deps app
+
check:: tests
clean:: clean-crashdump
@@ -101,7 +115,7 @@ distclean-tmp:
help::
$(verbose) printf "%s\n" \
"erlang.mk (version $(ERLANG_MK_VERSION)) is distributed under the terms of the ISC License." \
- "Copyright (c) 2013-2015 Loïc Hoguin <[email protected]>" \
+ "Copyright (c) 2013-2016 Loïc Hoguin <[email protected]>" \
"" \
"Usage: [V=1] $(MAKE) [target]..." \
"" \
@@ -109,6 +123,8 @@ help::
" all Run deps, app and rel targets in that order" \
" app Compile the project" \
" deps Fetch dependencies (if needed) and compile them" \
+ " fetch-deps Fetch dependencies recursively (if needed) without compiling them" \
+ " list-deps List dependencies recursively on stdout" \
" search q=... Search for a package in the built-in index" \
" rel Build a release for this project, if applicable" \
" docs Build the documentation for this project" \
@@ -147,24 +163,7 @@ else
core_native_path = $1
endif
-define core_http_get.erl
- ssl:start(),
- inets:start(),
- case httpc:request(get, {"$(2)", []}, [{autoredirect, true}], []) of
- {ok, {{_, 200, _}, _, Body}} ->
- case file:write_file("$(1)", Body) of
- ok -> ok;
- {error, R1} -> halt(R1)
- end;
- {error, R2} ->
- halt(R2)
- end,
- halt(0).
-endef
-
-define core_http_get
- $(call erlang,$(call core_http_get.erl,$(call core_native_path,$1),$2))
-endef
+core_http_get = curl -Lf$(if $(filter-out 0,$(V)),,s)o $(call core_native_path,$1) $2
core_eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
@@ -275,7 +274,15 @@ pkg_apns_description = Apple Push Notification Server for Erlang
pkg_apns_homepage = http://inaka.github.com/apns4erl
pkg_apns_fetch = git
pkg_apns_repo = https://github.com/inaka/apns4erl
-pkg_apns_commit = 1.0.4
+pkg_apns_commit = master
+
+PACKAGES += asciideck
+pkg_asciideck_name = asciideck
+pkg_asciideck_description = Asciidoc for Erlang.
+pkg_asciideck_homepage = https://ninenines.eu
+pkg_asciideck_fetch = git
+pkg_asciideck_repo = https://github.com/ninenines/asciideck
+pkg_asciideck_commit = master
PACKAGES += azdht
pkg_azdht_name = azdht
@@ -379,7 +386,7 @@ pkg_bitcask_description = because you need another a key/value storage engine
pkg_bitcask_homepage = https://github.com/basho/bitcask
pkg_bitcask_fetch = git
pkg_bitcask_repo = https://github.com/basho/bitcask
-pkg_bitcask_commit = master
+pkg_bitcask_commit = develop
PACKAGES += bitstore
pkg_bitstore_name = bitstore
@@ -451,7 +458,7 @@ pkg_cake_description = Really simple terminal colorization
pkg_cake_homepage = https://github.com/darach/cake-erl
pkg_cake_fetch = git
pkg_cake_repo = https://github.com/darach/cake-erl
-pkg_cake_commit = v0.1.2
+pkg_cake_commit = master
PACKAGES += carotene
pkg_carotene_name = carotene
@@ -509,6 +516,14 @@ pkg_chronos_fetch = git
pkg_chronos_repo = https://github.com/lehoff/chronos
pkg_chronos_commit = master
+PACKAGES += chumak
+pkg_chumak_name = chumak
+pkg_chumak_description = Pure Erlang implementation of ZeroMQ Message Transport Protocol.
+pkg_chumak_homepage = http://choven.ca
+pkg_chumak_fetch = git
+pkg_chumak_repo = https://github.com/chovencorp/chumak
+pkg_chumak_commit = master
+
PACKAGES += classifier
pkg_classifier_name = classifier
pkg_classifier_description = An Erlang Bayesian Filter and Text Classifier
@@ -787,7 +802,7 @@ pkg_cowboy_description = Small, fast and modular HTTP server.
pkg_cowboy_homepage = http://ninenines.eu
pkg_cowboy_fetch = git
pkg_cowboy_repo = https://github.com/ninenines/cowboy
-pkg_cowboy_commit = 1.0.1
+pkg_cowboy_commit = 1.0.4
PACKAGES += cowdb
pkg_cowdb_name = cowdb
@@ -803,7 +818,7 @@ pkg_cowlib_description = Support library for manipulating Web protocols.
pkg_cowlib_homepage = http://ninenines.eu
pkg_cowlib_fetch = git
pkg_cowlib_repo = https://github.com/ninenines/cowlib
-pkg_cowlib_commit = 1.0.1
+pkg_cowlib_commit = 1.0.2
PACKAGES += cpg
pkg_cpg_name = cpg
@@ -1045,14 +1060,6 @@ pkg_efene_fetch = git
pkg_efene_repo = https://github.com/efene/efene
pkg_efene_commit = master
-PACKAGES += eganglia
-pkg_eganglia_name = eganglia
-pkg_eganglia_description = Erlang library to interact with Ganglia
-pkg_eganglia_homepage = https://github.com/inaka/eganglia
-pkg_eganglia_fetch = git
-pkg_eganglia_repo = https://github.com/inaka/eganglia
-pkg_eganglia_commit = v0.9.1
-
PACKAGES += egeoip
pkg_egeoip_name = egeoip
pkg_egeoip_description = Erlang IP Geolocation module, currently supporting the MaxMind GeoLite City Database.
@@ -1067,7 +1074,7 @@ pkg_ehsa_description = Erlang HTTP server basic and digest authentication module
pkg_ehsa_homepage = https://bitbucket.org/a12n/ehsa
pkg_ehsa_fetch = hg
pkg_ehsa_repo = https://bitbucket.org/a12n/ehsa
-pkg_ehsa_commit = 2.0.4
+pkg_ehsa_commit = default
PACKAGES += ejabberd
pkg_ejabberd_name = ejabberd
@@ -1507,7 +1514,7 @@ pkg_erwa_description = A WAMP router and client written in Erlang.
pkg_erwa_homepage = https://github.com/bwegh/erwa
pkg_erwa_fetch = git
pkg_erwa_repo = https://github.com/bwegh/erwa
-pkg_erwa_commit = 0.1.1
+pkg_erwa_commit = master
PACKAGES += espec
pkg_espec_name = espec
@@ -1590,7 +1597,7 @@ pkg_evum_repo = https://github.com/msantos/evum
pkg_evum_commit = master
PACKAGES += exec
-pkg_exec_name = exec
+pkg_exec_name = erlexec
pkg_exec_description = Execute and control OS processes from Erlang/OTP.
pkg_exec_homepage = http://saleyn.github.com/erlexec
pkg_exec_fetch = git
@@ -1611,7 +1618,7 @@ pkg_exometer_description = Basic measurement objects and probe behavior
pkg_exometer_homepage = https://github.com/Feuerlabs/exometer
pkg_exometer_fetch = git
pkg_exometer_repo = https://github.com/Feuerlabs/exometer
-pkg_exometer_commit = 1.2
+pkg_exometer_commit = master
PACKAGES += exs1024
pkg_exs1024_name = exs1024
@@ -1675,7 +1682,15 @@ pkg_feeder_description = Stream parse RSS and Atom formatted XML feeds.
pkg_feeder_homepage = https://github.com/michaelnisi/feeder
pkg_feeder_fetch = git
pkg_feeder_repo = https://github.com/michaelnisi/feeder
-pkg_feeder_commit = v1.4.6
+pkg_feeder_commit = master
+
+PACKAGES += find_crate
+pkg_find_crate_name = find_crate
+pkg_find_crate_description = Find Rust libs and exes in Erlang application priv directory
+pkg_find_crate_homepage = https://github.com/goertzenator/find_crate
+pkg_find_crate_fetch = git
+pkg_find_crate_repo = https://github.com/goertzenator/find_crate
+pkg_find_crate_commit = master
PACKAGES += fix
pkg_fix_name = fix
@@ -1837,6 +1852,14 @@ pkg_gen_unix_fetch = git
pkg_gen_unix_repo = https://github.com/msantos/gen_unix
pkg_gen_unix_commit = master
+PACKAGES += geode
+pkg_geode_name = geode
+pkg_geode_description = geohash/proximity lookup in pure, uncut erlang.
+pkg_geode_homepage = https://github.com/bradfordw/geode
+pkg_geode_fetch = git
+pkg_geode_repo = https://github.com/bradfordw/geode
+pkg_geode_commit = master
+
PACKAGES += getopt
pkg_getopt_name = getopt
pkg_getopt_description = Module to parse command line arguments using the GNU getopt syntax
@@ -1995,7 +2018,7 @@ pkg_ibrowse_description = Erlang HTTP client
pkg_ibrowse_homepage = https://github.com/cmullaparthi/ibrowse
pkg_ibrowse_fetch = git
pkg_ibrowse_repo = https://github.com/cmullaparthi/ibrowse
-pkg_ibrowse_commit = v4.1.1
+pkg_ibrowse_commit = master
PACKAGES += ierlang
pkg_ierlang_name = ierlang
@@ -2051,7 +2074,7 @@ pkg_jamdb_sybase_description = Erlang driver for SAP Sybase ASE
pkg_jamdb_sybase_homepage = https://github.com/erlangbureau/jamdb_sybase
pkg_jamdb_sybase_fetch = git
pkg_jamdb_sybase_repo = https://github.com/erlangbureau/jamdb_sybase
-pkg_jamdb_sybase_commit = 0.6.0
+pkg_jamdb_sybase_commit = master
PACKAGES += jerg
pkg_jerg_name = jerg
@@ -2064,9 +2087,9 @@ pkg_jerg_commit = master
PACKAGES += jesse
pkg_jesse_name = jesse
pkg_jesse_description = jesse (JSon Schema Erlang) is an implementation of a json schema validator for Erlang.
-pkg_jesse_homepage = https://github.com/klarna/jesse
+pkg_jesse_homepage = https://github.com/for-GET/jesse
pkg_jesse_fetch = git
-pkg_jesse_repo = https://github.com/klarna/jesse
+pkg_jesse_repo = https://github.com/for-GET/jesse
pkg_jesse_commit = master
PACKAGES += jiffy
@@ -2083,7 +2106,7 @@ pkg_jiffy_v_description = JSON validation utility
pkg_jiffy_v_homepage = https://github.com/shizzard/jiffy-v
pkg_jiffy_v_fetch = git
pkg_jiffy_v_repo = https://github.com/shizzard/jiffy-v
-pkg_jiffy_v_commit = 0.3.3
+pkg_jiffy_v_commit = master
PACKAGES += jobs
pkg_jobs_name = jobs
@@ -2091,7 +2114,7 @@ pkg_jobs_description = a Job scheduler for load regulation
pkg_jobs_homepage = https://github.com/esl/jobs
pkg_jobs_fetch = git
pkg_jobs_repo = https://github.com/esl/jobs
-pkg_jobs_commit = 0.3
+pkg_jobs_commit = master
PACKAGES += joxa
pkg_joxa_name = joxa
@@ -2101,6 +2124,14 @@ pkg_joxa_fetch = git
pkg_joxa_repo = https://github.com/joxa/joxa
pkg_joxa_commit = master
+PACKAGES += jsone
+pkg_jsone_name = jsone
+pkg_jsone_description = An Erlang library for encoding, decoding JSON data.
+pkg_jsone_homepage = https://github.com/sile/jsone.git
+pkg_jsone_fetch = git
+pkg_jsone_repo = https://github.com/sile/jsone.git
+pkg_jsone_commit = master
+
PACKAGES += jsonerl
pkg_jsonerl_name = jsonerl
pkg_jsonerl_description = yet another but slightly different erlang <-> json encoder/decoder
@@ -2157,6 +2188,14 @@ pkg_kafka_fetch = git
pkg_kafka_repo = https://github.com/wooga/kafka-erlang
pkg_kafka_commit = master
+PACKAGES += kafka_protocol
+pkg_kafka_protocol_name = kafka_protocol
+pkg_kafka_protocol_description = Kafka protocol Erlang library
+pkg_kafka_protocol_homepage = https://github.com/klarna/kafka_protocol
+pkg_kafka_protocol_fetch = git
+pkg_kafka_protocol_repo = https://github.com/klarna/kafka_protocol.git
+pkg_kafka_protocol_commit = master
+
PACKAGES += kai
pkg_kai_name = kai
pkg_kai_description = DHT storage by Takeshi Inoue
@@ -2299,7 +2338,7 @@ pkg_lasse_description = SSE handler for Cowboy
pkg_lasse_homepage = https://github.com/inaka/lasse
pkg_lasse_fetch = git
pkg_lasse_repo = https://github.com/inaka/lasse
-pkg_lasse_commit = 0.1.0
+pkg_lasse_commit = master
PACKAGES += ldap
pkg_ldap_name = ldap
@@ -2755,7 +2794,7 @@ pkg_octopus_description = Small and flexible pool manager written in Erlang
pkg_octopus_homepage = https://github.com/erlangbureau/octopus
pkg_octopus_fetch = git
pkg_octopus_repo = https://github.com/erlangbureau/octopus
-pkg_octopus_commit = 1.0.0
+pkg_octopus_commit = master
PACKAGES += of_protocol
pkg_of_protocol_name = of_protocol
@@ -2827,7 +2866,7 @@ pkg_pegjs_description = An implementation of PEG.js grammar for Erlang.
pkg_pegjs_homepage = https://github.com/dmitriid/pegjs
pkg_pegjs_fetch = git
pkg_pegjs_repo = https://github.com/dmitriid/pegjs
-pkg_pegjs_commit = 0.3
+pkg_pegjs_commit = master
PACKAGES += percept2
pkg_percept2_name = percept2
@@ -2995,7 +3034,7 @@ pkg_qdate_description = Date, time, and timezone parsing, formatting, and conver
pkg_qdate_homepage = https://github.com/choptastic/qdate
pkg_qdate_fetch = git
pkg_qdate_repo = https://github.com/choptastic/qdate
-pkg_qdate_commit = 0.4.0
+pkg_qdate_commit = master
PACKAGES += qrcode
pkg_qrcode_name = qrcode
@@ -3067,7 +3106,7 @@ pkg_ranch_description = Socket acceptor pool for TCP protocols.
pkg_ranch_homepage = http://ninenines.eu
pkg_ranch_fetch = git
pkg_ranch_repo = https://github.com/ninenines/ranch
-pkg_ranch_commit = 1.1.0
+pkg_ranch_commit = 1.2.1
PACKAGES += rbeacon
pkg_rbeacon_name = rbeacon
@@ -3107,7 +3146,7 @@ pkg_recon_description = Collection of functions and scripts to debug Erlang in p
pkg_recon_homepage = https://github.com/ferd/recon
pkg_recon_fetch = git
pkg_recon_repo = https://github.com/ferd/recon
-pkg_recon_commit = 2.2.1
+pkg_recon_commit = master
PACKAGES += record_info
pkg_record_info_name = record_info
@@ -3301,6 +3340,14 @@ pkg_rlimit_fetch = git
pkg_rlimit_repo = https://github.com/jlouis/rlimit
pkg_rlimit_commit = master
+PACKAGES += rust_mk
+pkg_rust_mk_name = rust_mk
+pkg_rust_mk_description = Build Rust crates in an Erlang application
+pkg_rust_mk_homepage = https://github.com/goertzenator/rust.mk
+pkg_rust_mk_fetch = git
+pkg_rust_mk_repo = https://github.com/goertzenator/rust.mk
+pkg_rust_mk_commit = master
+
PACKAGES += safetyvalve
pkg_safetyvalve_name = safetyvalve
pkg_safetyvalve_description = A safety valve for your erlang node
@@ -3371,7 +3418,7 @@ pkg_shotgun_description = better than just a gun
pkg_shotgun_homepage = https://github.com/inaka/shotgun
pkg_shotgun_fetch = git
pkg_shotgun_repo = https://github.com/inaka/shotgun
-pkg_shotgun_commit = 0.1.0
+pkg_shotgun_commit = master
PACKAGES += sidejob
pkg_sidejob_name = sidejob
@@ -3429,6 +3476,14 @@ pkg_skel_fetch = git
pkg_skel_repo = https://github.com/ParaPhrase/skel
pkg_skel_commit = master
+PACKAGES += slack
+pkg_slack_name = slack
+pkg_slack_description = Minimal slack notification OTP library.
+pkg_slack_homepage = https://github.com/DonBranson/slack
+pkg_slack_fetch = git
+pkg_slack_repo = https://github.com/DonBranson/slack.git
+pkg_slack_commit = master
+
PACKAGES += smother
pkg_smother_name = smother
pkg_smother_description = Extended code coverage metrics for Erlang.
@@ -3437,6 +3492,14 @@ pkg_smother_fetch = git
pkg_smother_repo = https://github.com/ramsay-t/Smother
pkg_smother_commit = master
+PACKAGES += snappyer
+pkg_snappyer_name = snappyer
+pkg_snappyer_description = Snappy as nif for Erlang
+pkg_snappyer_homepage = https://github.com/zmstone/snappyer
+pkg_snappyer_fetch = git
+pkg_snappyer_repo = https://github.com/zmstone/snappyer.git
+pkg_snappyer_commit = master
+
PACKAGES += social
pkg_social_name = social
pkg_social_description = Cowboy handler for social login via OAuth2 providers
@@ -3591,7 +3654,7 @@ pkg_sync_commit = master
PACKAGES += syn
pkg_syn_name = syn
-pkg_syn_description = A global process registry for Erlang.
+pkg_syn_description = A global Process Registry and Process Group manager for Erlang.
pkg_syn_homepage = https://github.com/ostinelli/syn
pkg_syn_fetch = git
pkg_syn_repo = https://github.com/ostinelli/syn
@@ -3755,7 +3818,7 @@ pkg_unicorn_description = Generic configuration server
pkg_unicorn_homepage = https://github.com/shizzard/unicorn
pkg_unicorn_fetch = git
pkg_unicorn_repo = https://github.com/shizzard/unicorn
-pkg_unicorn_commit = 0.3.0
+pkg_unicorn_commit = master
PACKAGES += unsplit
pkg_unsplit_name = unsplit
@@ -3771,7 +3834,7 @@ pkg_uuid_description = Erlang UUID Implementation
pkg_uuid_homepage = https://github.com/okeuday/uuid
pkg_uuid_fetch = git
pkg_uuid_repo = https://github.com/okeuday/uuid
-pkg_uuid_commit = v1.4.0
+pkg_uuid_commit = master
PACKAGES += ux
pkg_ux_name = ux
@@ -3891,7 +3954,7 @@ pkg_worker_pool_description = a simple erlang worker pool
pkg_worker_pool_homepage = https://github.com/inaka/worker_pool
pkg_worker_pool_fetch = git
pkg_worker_pool_repo = https://github.com/inaka/worker_pool
-pkg_worker_pool_commit = 1.0.3
+pkg_worker_pool_commit = master
PACKAGES += wrangler
pkg_wrangler_name = wrangler
@@ -3923,7 +3986,7 @@ pkg_xref_runner_description = Erlang Xref Runner (inspired in rebar xref)
pkg_xref_runner_homepage = https://github.com/inaka/xref_runner
pkg_xref_runner_fetch = git
pkg_xref_runner_repo = https://github.com/inaka/xref_runner
-pkg_xref_runner_commit = 0.2.3
+pkg_xref_runner_commit = master
PACKAGES += yamerl
pkg_yamerl_name = yamerl
@@ -3949,6 +4012,14 @@ pkg_yaws_fetch = git
pkg_yaws_repo = https://github.com/klacke/yaws
pkg_yaws_commit = master
+PACKAGES += zabbix_sender
+pkg_zabbix_sender_name = zabbix_sender
+pkg_zabbix_sender_description = Zabbix trapper for sending data to Zabbix in pure Erlang
+pkg_zabbix_sender_homepage = https://github.com/stalkermn/zabbix_sender
+pkg_zabbix_sender_fetch = git
+pkg_zabbix_sender_repo = https://github.com/stalkermn/zabbix_sender.git
+pkg_zabbix_sender_commit = master
+
PACKAGES += zab_engine
pkg_zab_engine_name = zab_engine
pkg_zab_engine_description = zab propotocol implement by erlang
@@ -3963,7 +4034,7 @@ pkg_zeta_description = HTTP access log parser in Erlang
pkg_zeta_homepage = https://github.com/s1n4/zeta
pkg_zeta_fetch = git
pkg_zeta_repo = https://github.com/s1n4/zeta
-pkg_zeta_commit =
+pkg_zeta_commit = master
PACKAGES += zippers
pkg_zippers_name = zippers
@@ -3997,7 +4068,7 @@ pkg_zucchini_fetch = git
pkg_zucchini_repo = https://github.com/devinus/zucchini
pkg_zucchini_commit = master
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: search
@@ -4024,7 +4095,7 @@ else
$(foreach p,$(PACKAGES),$(call pkg_print,$(p)))
endif
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: distclean-deps
@@ -4074,16 +4145,38 @@ dep_verbose = $(dep_verbose_$(V))
# Core targets.
-ifneq ($(SKIP_DEPS),)
-deps::
+ifdef IS_APP
+apps::
else
-deps:: $(ALL_DEPS_DIRS)
-ifndef IS_APP
+apps:: $(ALL_APPS_DIRS)
+ifeq ($(IS_APP)$(IS_DEP),)
+ $(verbose) rm -f $(ERLANG_MK_TMP)/apps.log
+endif
+ $(verbose) mkdir -p $(ERLANG_MK_TMP)
+# Create ebin directory for all apps to make sure Erlang recognizes them
+# as proper OTP applications when using -include_lib. This is a temporary
+# fix, a proper fix would be to compile apps/* in the right order.
+ $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
+ mkdir -p $$dep/ebin || exit $$?; \
+ done
$(verbose) for dep in $(ALL_APPS_DIRS) ; do \
- $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
+ if grep -qs ^$$dep$$ $(ERLANG_MK_TMP)/apps.log; then \
+ :; \
+ else \
+ echo $$dep >> $(ERLANG_MK_TMP)/apps.log; \
+ $(MAKE) -C $$dep IS_APP=1 || exit $$?; \
+ fi \
done
endif
-ifneq ($(IS_DEP),1)
+
+ifneq ($(SKIP_DEPS),)
+deps::
+else
+ifeq ($(ALL_DEPS_DIRS),)
+deps:: apps
+else
+deps:: $(ALL_DEPS_DIRS) apps
+ifeq ($(IS_APP)$(IS_DEP),)
$(verbose) rm -f $(ERLANG_MK_TMP)/deps.log
endif
$(verbose) mkdir -p $(ERLANG_MK_TMP)
@@ -4101,6 +4194,7 @@ endif
fi \
done
endif
+endif
# Deps related targets.
@@ -4170,7 +4264,7 @@ define dep_autopatch_fetch_rebar
if [ ! -d $(ERLANG_MK_TMP)/rebar ]; then \
git clone -q -n -- https://github.com/rebar/rebar $(ERLANG_MK_TMP)/rebar; \
cd $(ERLANG_MK_TMP)/rebar; \
- git checkout -q 791db716b5a3a7671e0b351f95ddf24b848ee173; \
+ git checkout -q 576e12171ab8d69b048b827b92aa65d067deea01; \
$(MAKE); \
cd -; \
fi
@@ -4187,6 +4281,7 @@ endef
define dep_autopatch_rebar.erl
application:load(rebar),
application:set_env(rebar, log_level, debug),
+ rmemo:start(),
Conf1 = case file:consult("$(call core_native_path,$(DEPS_DIR)/$1/rebar.config)") of
{ok, Conf0} -> Conf0;
_ -> []
@@ -4340,9 +4435,9 @@ define dep_autopatch_rebar.erl
[] -> ok;
_ ->
Write("\npre-app::\n\t$$\(MAKE) -f c_src/Makefile.erlang.mk\n"),
- PortSpecWrite(io_lib:format("ERL_CFLAGS = -finline-functions -Wall -fPIC -I ~s/erts-~s/include -I ~s\n",
+ PortSpecWrite(io_lib:format("ERL_CFLAGS ?= -finline-functions -Wall -fPIC -I \\"~s/erts-~s/include\\" -I \\"~s\\"\n",
[code:root_dir(), erlang:system_info(version), code:lib_dir(erl_interface, include)])),
- PortSpecWrite(io_lib:format("ERL_LDFLAGS = -L ~s -lerl_interface -lei\n",
+ PortSpecWrite(io_lib:format("ERL_LDFLAGS ?= -L \\"~s\\" -lerl_interface -lei\n",
[code:lib_dir(erl_interface, lib)])),
[PortSpecWrite(["\n", E, "\n"]) || E <- OsEnv],
FilterEnv = fun(Env) ->
@@ -4381,7 +4476,7 @@ define dep_autopatch_rebar.erl
"%.o: %.C\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
"%.o: %.cc\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
"%.o: %.cpp\n\t$$\(CXX) -c -o $$\@ $$\< $$\(CXXFLAGS) $$\(ERL_CFLAGS) $$\(DRV_CFLAGS) $$\(EXE_CFLAGS)\n\n",
- [[Output, ": ", K, " = ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
+ [[Output, ": ", K, " += ", ShellToMk(V), "\n"] || {K, V} <- lists:reverse(MergeEnv(FilterEnv(Env)))],
Output, ": $$\(foreach ext,.c .C .cc .cpp,",
"$$\(patsubst %$$\(ext),%.o,$$\(filter %$$\(ext),$$\(wildcard", Input, "))))\n",
"\t$$\(CC) -o $$\@ $$\? $$\(LDFLAGS) $$\(ERL_LDFLAGS) $$\(DRV_LDFLAGS) $$\(EXE_LDFLAGS)",
@@ -4504,21 +4599,12 @@ define dep_fetch_cp
cp -R $(call dep_repo,$(1)) $(DEPS_DIR)/$(call dep_name,$(1));
endef
-define dep_fetch_hex.erl
- ssl:start(),
- inets:start(),
- {ok, {{_, 200, _}, _, Body}} = httpc:request(get,
- {"https://s3.amazonaws.com/s3.hex.pm/tarballs/$(1)-$(2).tar", []},
- [], [{body_format, binary}]),
- {ok, Files} = erl_tar:extract({binary, Body}, [memory]),
- {_, Source} = lists:keyfind("contents.tar.gz", 1, Files),
- ok = erl_tar:extract({binary, Source}, [{cwd, "$(call core_native_path,$(DEPS_DIR)/$1)"}, compressed]),
- halt()
-endef
-
# Hex only has a package version. No need to look in the Erlang.mk packages.
define dep_fetch_hex
- $(call erlang,$(call dep_fetch_hex.erl,$(1),$(strip $(word 2,$(dep_$(1))))));
+ mkdir -p $(ERLANG_MK_TMP)/hex $(DEPS_DIR)/$1; \
+ $(call core_http_get,$(ERLANG_MK_TMP)/hex/$1.tar,\
+ https://s3.amazonaws.com/s3.hex.pm/tarballs/$1-$(strip $(word 2,$(dep_$1))).tar); \
+ tar -xOf $(ERLANG_MK_TMP)/hex/$1.tar contents.tar.gz | tar -C $(DEPS_DIR)/$1 -xzf -;
endef
define dep_fetch_fail
@@ -4609,6 +4695,15 @@ distclean-deps:
$(gen_verbose) rm -rf $(DEPS_DIR)
endif
+# Forward-declare variables used in core/deps-tools.mk. This is required
+# in case plugins use them.
+
+ERLANG_MK_RECURSIVE_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-deps-list.log
+ERLANG_MK_RECURSIVE_DOC_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-doc-deps-list.log
+ERLANG_MK_RECURSIVE_REL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-rel-deps-list.log
+ERLANG_MK_RECURSIVE_TEST_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-test-deps-list.log
+ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST = $(ERLANG_MK_TMP)/recursive-shell-deps-list.log
+
# External plugins.
DEP_PLUGINS ?=
@@ -4624,7 +4719,7 @@ $(foreach p,$(DEP_PLUGINS),\
$(call core_dep_plugin,$p,$(firstword $(subst /, ,$p))),\
$(call core_dep_plugin,$p/plugins.mk,$p))))
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
# Configuration.
@@ -4632,6 +4727,7 @@ $(foreach p,$(DEP_PLUGINS),\
DTL_FULL_PATH ?=
DTL_PATH ?= templates/
DTL_SUFFIX ?= _dtl
+DTL_OPTS ?=
# Verbosity.
@@ -4640,31 +4736,38 @@ dtl_verbose = $(dtl_verbose_$(V))
# Core targets.
+DTL_PATH := $(abspath $(DTL_PATH))
DTL_FILES := $(sort $(call core_find,$(DTL_PATH),*.dtl))
ifneq ($(DTL_FILES),)
-ifdef DTL_FULL_PATH
-BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(subst /,_,$(DTL_FILES:$(DTL_PATH)%=%))))
-else
-BEAM_FILES += $(addprefix ebin/,$(patsubst %.dtl,%_dtl.beam,$(notdir $(DTL_FILES))))
-endif
+DTL_NAMES = $(addsuffix $(DTL_SUFFIX),$(DTL_FILES:$(DTL_PATH)/%.dtl=%))
+DTL_MODULES = $(if $(DTL_FULL_PATH),$(subst /,_,$(DTL_NAMES)),$(notdir $(DTL_NAMES)))
+BEAM_FILES += $(addsuffix .beam,$(addprefix ebin/,$(DTL_MODULES)))
+ifneq ($(words $(DTL_FILES)),0)
# Rebuild templates when the Makefile changes.
-$(DTL_FILES): $(MAKEFILE_LIST)
+$(ERLANG_MK_TMP)/last-makefile-change-erlydtl: $(MAKEFILE_LIST)
+ @mkdir -p $(ERLANG_MK_TMP)
+ @if test -f $@; then \
+ touch $(DTL_FILES); \
+ fi
@touch $@
+ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change-erlydtl
+endif
+
define erlydtl_compile.erl
[begin
Module0 = case "$(strip $(DTL_FULL_PATH))" of
"" ->
filename:basename(F, ".dtl");
_ ->
- "$(DTL_PATH)" ++ F2 = filename:rootname(F, ".dtl"),
+ "$(DTL_PATH)/" ++ F2 = filename:rootname(F, ".dtl"),
re:replace(F2, "/", "_", [{return, list}, global])
end,
Module = list_to_atom(string:to_lower(Module0) ++ "$(DTL_SUFFIX)"),
- case erlydtl:compile(F, Module, [{out_dir, "ebin/"}, return_errors, {doc_root, "templates"}]) of
+ case erlydtl:compile(F, Module, [$(DTL_OPTS)] ++ [{out_dir, "ebin/"}, return_errors]) of
ok -> ok;
{ok, _} -> ok
end
@@ -4674,11 +4777,12 @@ endef
ebin/$(PROJECT).app:: $(DTL_FILES) | ebin/
$(if $(strip $?),\
- $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$?),-pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
+ $(dtl_verbose) $(call erlang,$(call erlydtl_compile.erl,$(call core_native_path,$?)),\
+ -pa ebin/ $(DEPS_DIR)/erlydtl/ebin/))
endif
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
# Verbosity.
@@ -4705,13 +4809,12 @@ define compile_proto.erl
halt().
endef
-# @todo Convert like erlydtl was.
-# @todo Give access to ALL_SRC_FILES.
-
+ifneq ($(wildcard src/),)
ebin/$(PROJECT).app:: $(sort $(call core_find,src/,*.proto))
$(if $(strip $?),$(call compile_proto,$?))
+endif
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: clean-app
@@ -4772,7 +4875,7 @@ app:: clean deps $(PROJECT).d
$(verbose) $(MAKE) --no-print-directory app-build
endif
-ifeq ($(wildcard src/$(PROJECT)_app.erl),)
+ifeq ($(wildcard src/$(PROJECT_MOD).erl),)
define app_file
{application, $(PROJECT), [
{description, "$(PROJECT_DESCRIPTION)"},
@@ -4780,7 +4883,8 @@ define app_file
{id$(comma)$(space)"$(1)"}$(comma))
{modules, [$(call comma_list,$(2))]},
{registered, []},
- {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]}
+ {applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
+ {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
]}.
endef
else
@@ -4792,7 +4896,8 @@ define app_file
{modules, [$(call comma_list,$(2))]},
{registered, [$(call comma_list,$(PROJECT)_sup $(PROJECT_REGISTERED))]},
{applications, [$(call comma_list,kernel stdlib $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS))]},
- {mod, {$(PROJECT)_app, []}}
+ {mod, {$(PROJECT_MOD), []}},
+ {env, $(subst \,\\,$(PROJECT_ENV))}$(if $(findstring {,$(PROJECT_APP_EXTRA_KEYS)),$(comma)$(newline)$(tab)$(subst \,\\,$(PROJECT_APP_EXTRA_KEYS)),)
]}.
endef
endif
@@ -4802,7 +4907,6 @@ app-build: ebin/$(PROJECT).app
# Source files.
-# @todo have "all test/ files" also.
ALL_SRC_FILES := $(sort $(call core_find,src/,*))
ERL_FILES := $(filter %.erl,$(ALL_SRC_FILES))
@@ -4848,7 +4952,7 @@ YRL_ERL_FILES = $(addprefix src/,$(patsubst %.yrl,%.erl,$(notdir $(YRL_FILES))))
ERL_FILES += $(YRL_ERL_FILES)
$(PROJECT).d:: $(XRL_FILES) $(YRL_FILES)
- $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $?)
+ $(if $(strip $?),$(xyrl_verbose) erlc -v -o src/ $(YRL_ERLC_OPTS) $?)
# Erlang and Core Erlang files.
@@ -4930,14 +5034,23 @@ define makedep.erl
endef
ifeq ($(if $(NO_MAKEDEP),$(wildcard $(PROJECT).d),),)
-# @todo Not src/*.hrl?
-$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl)
+$(PROJECT).d:: $(ERL_FILES) $(call core_find,include/,*.hrl) $(MAKEFILE_LIST)
$(makedep_verbose) $(call erlang,$(call makedep.erl,$@))
endif
+ifneq ($(words $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES)),0)
# Rebuild everything when the Makefile changes.
-$(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(MAKEFILE_LIST)
- @touch $@
+$(ERLANG_MK_TMP)/last-makefile-change: $(MAKEFILE_LIST)
+ $(verbose) mkdir -p $(ERLANG_MK_TMP)
+ $(verbose) if test -f $@; then \
+ touch $(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES); \
+ touch -c $(PROJECT).d; \
+ fi
+ $(verbose) touch $@
+
+$(ERL_FILES) $(CORE_FILES) $(ASN1_FILES) $(MIB_FILES) $(XRL_FILES) $(YRL_FILES):: $(ERLANG_MK_TMP)/last-makefile-change
+ebin/$(PROJECT).app:: $(ERLANG_MK_TMP)/last-makefile-change
+endif
-include $(PROJECT).d
@@ -4958,16 +5071,16 @@ ebin/$(PROJECT).app:: $(ERL_FILES) $(CORE_FILES) $(wildcard src/$(PROJECT).app.s
$(eval MODULES := $(patsubst %,'%',$(sort $(notdir $(basename \
$(filter-out $(ERLC_EXCLUDE_PATHS),$(ERL_FILES) $(CORE_FILES) $(BEAM_FILES)))))))
ifeq ($(wildcard src/$(PROJECT).app.src),)
- $(app_verbose) printf "$(subst $(newline),\n,$(subst ",\",$(call app_file,$(GITDESCRIBE),$(MODULES))))" \
+ $(app_verbose) printf '$(subst %,%%,$(subst $(newline),\n,$(subst ','\'',$(call app_file,$(GITDESCRIBE),$(MODULES)))))' \
> ebin/$(PROJECT).app
else
- $(verbose) if [ -z "$$(grep -E '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
+ $(verbose) if [ -z "$$(grep -e '^[^%]*{\s*modules\s*,' src/$(PROJECT).app.src)" ]; then \
echo "Empty modules entry not found in $(PROJECT).app.src. Please consult the erlang.mk README for instructions." >&2; \
exit 1; \
fi
$(appsrc_verbose) cat src/$(PROJECT).app.src \
| sed "s/{[[:space:]]*modules[[:space:]]*,[[:space:]]*\[\]}/{modules, \[$(call comma_list,$(MODULES))\]}/" \
- | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(GITDESCRIBE)\"}/" \
+ | sed "s/{id,[[:space:]]*\"git\"}/{id, \"$(subst /,\/,$(GITDESCRIBE))\"}/" \
> ebin/$(PROJECT).app
endif
@@ -4982,6 +5095,7 @@ clean-app:
endif
+# Copyright (c) 2016, Loïc Hoguin <[email protected]>
# Copyright (c) 2015, Viktor Söderqvist <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
@@ -5002,7 +5116,7 @@ doc-deps: $(ALL_DOC_DEPS_DIRS)
$(verbose) for dep in $(ALL_DOC_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
endif
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: rel-deps
@@ -5022,7 +5136,7 @@ rel-deps: $(ALL_REL_DEPS_DIRS)
$(verbose) for dep in $(ALL_REL_DEPS_DIRS) ; do $(MAKE) -C $$dep; done
endif
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: test-deps test-dir test-build clean-test-dir
@@ -5077,7 +5191,7 @@ ifneq ($(wildcard $(TEST_DIR)/*.beam),)
endif
endif
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: rebar.config
@@ -5085,7 +5199,7 @@ endif
# We strip out -Werror because we don't want to fail due to
# warnings when used as a dependency.
-compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/')
+compat_prepare_erlc_opts = $(shell echo "$1" | sed 's/, */,/g')
define compat_convert_erlc_opts
$(if $(filter-out -Werror,$1),\
@@ -5098,8 +5212,12 @@ define compat_erlc_opts_to_list
endef
define compat_rebar_config
-{deps, [$(call comma_list,$(foreach d,$(DEPS),\
- {$(call dep_name,$d),".*",{git,"$(call dep_repo,$d)","$(call dep_commit,$d)"}}))]}.
+{deps, [
+$(call comma_list,$(foreach d,$(DEPS),\
+ $(if $(filter hex,$(call dep_fetch,$d)),\
+ {$(call dep_name,$d)$(comma)"$(call dep_repo,$d)"},\
+ {$(call dep_name,$d)$(comma)".*"$(comma){git,"$(call dep_repo,$d)"$(comma)"$(call dep_commit,$d)"}})))
+]}.
{erl_opts, $(call compat_erlc_opts_to_list,$(ERLC_OPTS))}.
endef
@@ -5109,54 +5227,88 @@ $(eval export _compat_rebar_config)
rebar.config:
$(gen_verbose) echo "$${_compat_rebar_config}" > rebar.config
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc
+ifeq ($(filter asciideck,$(DEPS) $(DOC_DEPS)),asciideck)
-MAN_INSTALL_PATH ?= /usr/local/share/man
-MAN_SECTIONS ?= 3 7
+.PHONY: asciidoc asciidoc-guide asciidoc-manual install-asciidoc distclean-asciidoc-guide distclean-asciidoc-manual
+
+# Core targets.
docs:: asciidoc
+distclean:: distclean-asciidoc-guide distclean-asciidoc-manual
+
+# Plugin-specific targets.
+
asciidoc: asciidoc-guide asciidoc-manual
+# User guide.
+
ifeq ($(wildcard doc/src/guide/book.asciidoc),)
asciidoc-guide:
else
-asciidoc-guide: distclean-asciidoc doc-deps
+asciidoc-guide: distclean-asciidoc-guide doc-deps
a2x -v -f pdf doc/src/guide/book.asciidoc && mv doc/src/guide/book.pdf doc/guide.pdf
a2x -v -f chunked doc/src/guide/book.asciidoc && mv doc/src/guide/book.chunked/ doc/html/
+
+distclean-asciidoc-guide:
+ $(gen_verbose) rm -rf doc/html/ doc/guide.pdf
endif
-ifeq ($(wildcard doc/src/manual/*.asciidoc),)
+# Man pages.
+
+ASCIIDOC_MANUAL_FILES := $(wildcard doc/src/manual/*.asciidoc)
+
+ifeq ($(ASCIIDOC_MANUAL_FILES),)
asciidoc-manual:
else
-asciidoc-manual: distclean-asciidoc doc-deps
- for f in doc/src/manual/*.asciidoc ; do \
- a2x -v -f manpage $$f ; \
- done
- for s in $(MAN_SECTIONS); do \
- mkdir -p doc/man$$s/ ; \
- mv doc/src/manual/*.$$s doc/man$$s/ ; \
- gzip doc/man$$s/*.$$s ; \
- done
+
+# Configuration.
+
+MAN_INSTALL_PATH ?= /usr/local/share/man
+MAN_SECTIONS ?= 3 7
+MAN_PROJECT ?= $(shell echo $(PROJECT) | sed 's/^./\U&\E/')
+MAN_VERSION ?= $(PROJECT_VERSION)
+
+# Plugin-specific targets.
+
+define asciidoc2man.erl
+try
+ [begin
+ ok = asciideck:to_manpage(asciideck:parse_file(F), #{
+ compress => gzip,
+ outdir => filename:dirname(F),
+ extra2 => "$(MAN_PROJECT) $(MAN_VERSION)",
+ extra3 => "$(MAN_PROJECT) Function Reference"
+ })
+ end || F <- [$(shell echo $(addprefix $(comma)\",$(addsuffix \",$1)) | sed 's/^.//')]],
+ halt(0)
+catch _:_ ->
+ halt(1)
+end.
+endef
+
+asciidoc-manual:: doc-deps
+
+asciidoc-manual:: $(ASCIIDOC_MANUAL_FILES)
+ $(call erlang,$(call asciidoc2man.erl,$?))
+ $(foreach s,$(MAN_SECTIONS),mkdir -p doc/man$s/ && mv doc/src/manual/*.$s.gz doc/man$s/;)
install-docs:: install-asciidoc
install-asciidoc: asciidoc-manual
- for s in $(MAN_SECTIONS); do \
- mkdir -p $(MAN_INSTALL_PATH)/man$$s/ ; \
- install -g `id -u` -o `id -g` -m 0644 doc/man$$s/*.gz $(MAN_INSTALL_PATH)/man$$s/ ; \
- done
-endif
-
-distclean:: distclean-asciidoc
+ $(foreach s,$(MAN_SECTIONS),\
+ mkdir -p $(MAN_INSTALL_PATH)/man$s/ && \
+ install -g `id -u` -o `id -g` -m 0644 doc/man$s/*.gz $(MAN_INSTALL_PATH)/man$s/;)
-distclean-asciidoc:
- $(gen_verbose) rm -rf doc/html/ doc/guide.pdf doc/man3/ doc/man7/
+distclean-asciidoc-manual:
+ $(gen_verbose) rm -rf $(addprefix doc/man,$(MAN_SECTIONS))
+endif
+endif
-# Copyright (c) 2014-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2014-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: bootstrap bootstrap-lib bootstrap-rel new list-templates
@@ -5169,8 +5321,8 @@ help::
" bootstrap Generate a skeleton of an OTP application" \
" bootstrap-lib Generate a skeleton of an OTP library" \
" bootstrap-rel Generate the files needed to build a release" \
- " new-app n=NAME Create a new local OTP application NAME" \
- " new-lib n=NAME Create a new local OTP library NAME" \
+ " new-app in=NAME Create a new local OTP application NAME" \
+ " new-lib in=NAME Create a new local OTP library NAME" \
" new t=TPL n=NAME Generate a module NAME based on the template TPL" \
" new t=T n=N in=APP Generate a module NAME based on the template TPL in APP" \
" list-templates List available templates"
@@ -5213,7 +5365,7 @@ ifdef SP
define bs_Makefile
PROJECT = $p
PROJECT_DESCRIPTION = New project
-PROJECT_VERSION = 0.0.1
+PROJECT_VERSION = 0.1.0
# Whitespace to be used when creating files from templates.
SP = $(SP)
@@ -5223,7 +5375,7 @@ else
define bs_Makefile
PROJECT = $p
PROJECT_DESCRIPTION = New project
-PROJECT_VERSION = 0.0.1
+PROJECT_VERSION = 0.1.0
endef
endif
@@ -5231,7 +5383,7 @@ endif
define bs_apps_Makefile
PROJECT = $p
PROJECT_DESCRIPTION = New project
-PROJECT_VERSION = 0.0.1
+PROJECT_VERSION = 0.1.0
include $(call core_relpath,$(dir $(ERLANG_MK_FILENAME)),$(APPS_DIR)/app)/erlang.mk
endef
@@ -5251,7 +5403,7 @@ stop(_State) ->
endef
define bs_relx_config
-{release, {$p_release, "1"}, [$p]}.
+{release, {$p_release, "1"}, [$p, sasl, runtime_tools]}.
{extended_start_script, true}.
{sys_config, "rel/sys.config"}.
{vm_args, "rel/vm.args"}.
@@ -5330,6 +5482,11 @@ code_change(_OldVsn, State, _Extra) ->
{ok, State}.
endef
+define tpl_module
+-module($(n)).
+-export([]).
+endef
+
define tpl_cowboy_http
-module($(n)).
-behaviour(cowboy_http_handler).
@@ -5612,7 +5769,7 @@ endif
list-templates:
$(verbose) echo Available templates: $(sort $(patsubst tpl_%,%,$(filter tpl_%,$(.VARIABLES))))
-# Copyright (c) 2014-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2014-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: clean-c_src distclean-c_src-env
@@ -5644,6 +5801,7 @@ ifeq ($(PLATFORM),msys2)
# We hardcode the compiler used on MSYS2. The default CC=cc does
# not produce working code. The "gcc" MSYS2 package also doesn't.
CC = /mingw64/bin/gcc
+ export CC
CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes
CXXFLAGS ?= -O3 -finline-functions -Wall
else ifeq ($(PLATFORM),darwin)
@@ -5846,29 +6004,35 @@ else
$(call render_template,bs_erl_nif,src/$n.erl)
endif
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: ci ci-prepare ci-setup distclean-kerl
CI_OTP ?=
+CI_HIPE ?=
+CI_ERLLVM ?=
+
+ifeq ($(CI_VM),native)
+ERLC_OPTS += +native
+TEST_ERLC_OPTS += +native
+else ifeq ($(CI_VM),erllvm)
+ERLC_OPTS += +native +'{hipe, [to_llvm]}'
+TEST_ERLC_OPTS += +native +'{hipe, [to_llvm]}'
+endif
-ifeq ($(strip $(CI_OTP)),)
+ifeq ($(strip $(CI_OTP) $(CI_HIPE) $(CI_ERLLVM)),)
ci::
else
-ifndef KERL
-KERL := $(shell which kerl 2>/dev/null)
-
ifeq ($(strip $(KERL)),)
-KERL := $(CURDIR)/.erlang.mk/kerl/kerl
-endif
+KERL := $(ERLANG_MK_TMP)/kerl/kerl
endif
export KERL
-KERL_GIT ?= https://github.com/yrashk/kerl
-KERL_COMMIT ?= 4e7c4349ddcd46ac11cd4cd50bfbda25f1f11ca2
+KERL_GIT ?= https://github.com/kerl/kerl
+KERL_COMMIT ?= master
KERL_MAKEFLAGS ?=
@@ -5876,25 +6040,32 @@ OTP_GIT ?= https://github.com/erlang/otp
CI_INSTALL_DIR ?= $(HOME)/erlang
-ci:: $(addprefix ci-,$(CI_OTP))
+ci:: $(addprefix ci-,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)) $(addsuffix -erllvm,$(CI_ERLLVM)))
-ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP))
+ci-prepare: $(addprefix $(CI_INSTALL_DIR)/,$(CI_OTP) $(addsuffix -native,$(CI_HIPE)))
ci-setup::
+ci-extra::
+
ci_verbose_0 = @echo " CI " $(1);
ci_verbose = $(ci_verbose_$(V))
define ci_target
-ci-$(1): $(CI_INSTALL_DIR)/$(1)
+ci-$1: $(CI_INSTALL_DIR)/$2
+ $(verbose) $(MAKE) --no-print-directory clean
$(ci_verbose) \
- PATH="$(CI_INSTALL_DIR)/$(1)/bin:$(PATH)" \
- CI_OTP_RELEASE="$(1)" \
- CT_OPTS="-label $(1)" \
- $(MAKE) clean ci-setup tests
+ PATH="$(CI_INSTALL_DIR)/$2/bin:$(PATH)" \
+ CI_OTP_RELEASE="$1" \
+ CT_OPTS="-label $1" \
+ CI_VM="$3" \
+ $(MAKE) ci-setup tests
+ $(verbose) $(MAKE) --no-print-directory ci-extra
endef
-$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp))))
+$(foreach otp,$(CI_OTP),$(eval $(call ci_target,$(otp),$(otp),otp)))
+$(foreach otp,$(CI_HIPE),$(eval $(call ci_target,$(otp)-native,$(otp)-native,native)))
+$(foreach otp,$(CI_ERLLVM),$(eval $(call ci_target,$(otp)-erllvm,$(otp)-native,erllvm)))
define ci_otp_target
ifeq ($(wildcard $(CI_INSTALL_DIR)/$(1)),)
@@ -5906,9 +6077,20 @@ endef
$(foreach otp,$(CI_OTP),$(eval $(call ci_otp_target,$(otp))))
+define ci_hipe_target
+ifeq ($(wildcard $(CI_INSTALL_DIR)/$1-native),)
+$(CI_INSTALL_DIR)/$1-native: $(KERL)
+ KERL_CONFIGURE_OPTIONS=--enable-native-libs \
+ MAKEFLAGS="$(KERL_MAKEFLAGS)" $(KERL) build git $(OTP_GIT) $1 $1-native
+ $(KERL) install $1-native $(CI_INSTALL_DIR)/$1-native
+endif
+endef
+
+$(foreach otp,$(sort $(CI_HIPE) $(CI_ERLLLVM)),$(eval $(call ci_hipe_target,$(otp))))
+
$(KERL):
$(verbose) mkdir -p $(ERLANG_MK_TMP)
- $(gen_verbose) git clone $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
+ $(gen_verbose) git clone --depth 1 $(KERL_GIT) $(ERLANG_MK_TMP)/kerl
$(verbose) cd $(ERLANG_MK_TMP)/kerl && git checkout $(KERL_COMMIT)
$(verbose) chmod +x $(KERL)
@@ -5926,7 +6108,7 @@ distclean-kerl:
$(gen_verbose) rm -rf $(KERL)
endif
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: ct apps-ct distclean-ct
@@ -5961,7 +6143,7 @@ help::
CT_RUN = ct_run \
-no_auto_compile \
-noinput \
- -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(TEST_DIR) \
+ -pa $(CURDIR)/ebin $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(TEST_DIR) \
-dir $(TEST_DIR) \
-logdir $(CURDIR)/logs
@@ -5974,8 +6156,14 @@ ct: test-build $(if $(IS_APP),,apps-ct)
endif
ifneq ($(ALL_APPS_DIRS),)
-apps-ct:
- $(verbose) for app in $(ALL_APPS_DIRS); do $(MAKE) -C $$app ct IS_APP=1; done
+define ct_app_target
+apps-ct-$1:
+ $(MAKE) -C $1 ct IS_APP=1
+endef
+
+$(foreach app,$(ALL_APPS_DIRS),$(eval $(call ct_app_target,$(app))))
+
+apps-ct: test-build $(addprefix apps-ct-,$(ALL_APPS_DIRS))
endif
ifndef t
@@ -6000,7 +6188,7 @@ $(foreach test,$(CT_SUITES),$(eval $(call ct_suite_target,$(test))))
distclean-ct:
$(gen_verbose) rm -rf $(CURDIR)/logs/
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: plt distclean-plt dialyze
@@ -6029,19 +6217,23 @@ help::
# Plugin-specific targets.
define filter_opts.erl
- Opts = binary:split(<<"$1">>, <<"-">>, [global]),
- Filtered = lists:reverse(lists:foldl(fun
- (O = <<"pa ", _/bits>>, Acc) -> [O|Acc];
- (O = <<"D ", _/bits>>, Acc) -> [O|Acc];
- (O = <<"I ", _/bits>>, Acc) -> [O|Acc];
- (_, Acc) -> Acc
- end, [], Opts)),
- io:format("~s~n", [[["-", O] || O <- Filtered]]),
+ Opts = init:get_plain_arguments(),
+ {Filtered, _} = lists:foldl(fun
+ (O, {Os, true}) -> {[O|Os], false};
+ (O = "-D", {Os, _}) -> {[O|Os], true};
+ (O = [\\$$-, \\$$D, _ | _], {Os, _}) -> {[O|Os], false};
+ (O = "-I", {Os, _}) -> {[O|Os], true};
+ (O = [\\$$-, \\$$I, _ | _], {Os, _}) -> {[O|Os], false};
+ (O = "-pa", {Os, _}) -> {[O|Os], true};
+ (_, Acc) -> Acc
+ end, {[], false}, Opts),
+ io:format("~s~n", [string:join(lists:reverse(Filtered), " ")]),
halt().
endef
$(DIALYZER_PLT): deps app
- $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) $(DEPS)
+ $(verbose) dialyzer --build_plt --apps erts kernel stdlib $(PLT_APPS) $(OTP_DEPS) $(LOCAL_DEPS) \
+ `test -f $(ERLANG_MK_TMP)/deps.log && cat $(ERLANG_MK_TMP)/deps.log`
plt: $(DIALYZER_PLT)
@@ -6053,9 +6245,9 @@ dialyze:
else
dialyze: $(DIALYZER_PLT)
endif
- $(verbose) dialyzer --no_native `$(call erlang,$(call filter_opts.erl,$(ERLC_OPTS)))` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
+ $(verbose) dialyzer --no_native `$(ERL) -eval "$(subst $(newline),,$(subst ",\",$(call filter_opts.erl)))" -extra $(ERLC_OPTS)` $(DIALYZER_DIRS) $(DIALYZER_OPTS)
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
.PHONY: distclean-edoc edoc
@@ -6080,23 +6272,23 @@ edoc: distclean-edoc doc-deps
distclean-edoc:
$(gen_verbose) rm -f doc/*.css doc/*.html doc/*.png doc/edoc-info
-# Copyright (c) 2014 Dave Cottlehuber <[email protected]>
+# Copyright (c) 2016, Loïc Hoguin <[email protected]>
+# Copyright (c) 2014, Dave Cottlehuber <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-.PHONY: distclean-escript escript
+.PHONY: distclean-escript escript escript-zip
# Configuration.
ESCRIPT_NAME ?= $(PROJECT)
-ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
+ESCRIPT_FILE ?= $(ESCRIPT_NAME)
-ESCRIPT_BEAMS ?= "ebin/*", "deps/*/ebin/*"
-ESCRIPT_SYS_CONFIG ?= "rel/sys.config"
-ESCRIPT_EMU_ARGS ?= -pa . \
- -sasl errlog_type error \
- -escript main $(ESCRIPT_NAME)
ESCRIPT_SHEBANG ?= /usr/bin/env escript
-ESCRIPT_STATIC ?= "deps/*/priv/**", "priv/**"
+ESCRIPT_COMMENT ?= This is an -*- erlang -*- file
+ESCRIPT_EMU_ARGS ?= -escript main $(ESCRIPT_NAME)
+
+ESCRIPT_ZIP ?= 7z a -tzip -mx=9 -mtc=off $(if $(filter-out 0,$(V)),,> /dev/null)
+ESCRIPT_ZIP_FILE ?= $(ERLANG_MK_TMP)/escript.zip
# Core targets.
@@ -6109,44 +6301,28 @@ help::
# Plugin-specific targets.
-# Based on https://github.com/synrc/mad/blob/master/src/mad_bundle.erl
-# Copyright (c) 2013 Maxim Sokhatsky, Synrc Research Center
-# Modified MIT License, https://github.com/synrc/mad/blob/master/LICENSE :
-# Software may only be used for the great good and the true happiness of all
-# sentient beings.
-
-define ESCRIPT_RAW
-'Read = fun(F) -> {ok, B} = file:read_file(filename:absname(F)), B end,'\
-'Files = fun(L) -> A = lists:concat([filelib:wildcard(X)||X<- L ]),'\
-' [F || F <- A, not filelib:is_dir(F) ] end,'\
-'Squash = fun(L) -> [{filename:basename(F), Read(F) } || F <- L ] end,'\
-'Zip = fun(A, L) -> {ok,{_,Z}} = zip:create(A, L, [{compress,all},memory]), Z end,'\
-'Ez = fun(Escript) ->'\
-' Static = Files([$(ESCRIPT_STATIC)]),'\
-' Beams = Squash(Files([$(ESCRIPT_BEAMS), $(ESCRIPT_SYS_CONFIG)])),'\
-' Archive = Beams ++ [{ "static.gz", Zip("static.gz", Static)}],'\
-' escript:create(Escript, [ $(ESCRIPT_OPTIONS)'\
-' {archive, Archive, [memory]},'\
-' {shebang, "$(ESCRIPT_SHEBANG)"},'\
-' {comment, "$(ESCRIPT_COMMENT)"},'\
-' {emu_args, " $(ESCRIPT_EMU_ARGS)"}'\
-' ]),'\
-' file:change_mode(Escript, 8#755)'\
-'end,'\
-'Ez("$(ESCRIPT_NAME)"),'\
-'halt().'
-endef
-
-ESCRIPT_COMMAND = $(subst ' ',,$(ESCRIPT_RAW))
+escript-zip:: deps app
+ $(verbose) mkdir -p $(dir $(ESCRIPT_ZIP))
+ $(verbose) rm -f $(ESCRIPT_ZIP_FILE)
+ $(gen_verbose) cd .. && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) $(PROJECT)/ebin/*
+ifneq ($(DEPS),)
+ $(verbose) cd $(DEPS_DIR) && $(ESCRIPT_ZIP) $(ESCRIPT_ZIP_FILE) \
+ `cat $(ERLANG_MK_TMP)/deps.log | sed 's/^$(subst /,\/,$(DEPS_DIR))\///' | sed 's/$$/\/ebin\/\*/'`
+endif
-escript:: distclean-escript deps app
- $(gen_verbose) $(ERL) -eval $(ESCRIPT_COMMAND)
+escript:: escript-zip
+ $(gen_verbose) printf "%s\n" \
+ "#!$(ESCRIPT_SHEBANG)" \
+ "%% $(ESCRIPT_COMMENT)" \
+ "%%! $(ESCRIPT_EMU_ARGS)" > $(ESCRIPT_FILE)
+ $(verbose) cat $(ESCRIPT_ZIP_FILE) >> $(ESCRIPT_FILE)
+ $(verbose) chmod +x $(ESCRIPT_FILE)
distclean-escript:
$(gen_verbose) rm -f $(ESCRIPT_NAME)
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# Copyright (c) 2014, Enrique Fernandez <[email protected]>
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.
.PHONY: eunit apps-eunit
@@ -6188,7 +6364,7 @@ define eunit.erl
halt()
endef
-EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin ebin
+EUNIT_ERL_OPTS += -pa $(TEST_DIR) $(DEPS_DIR)/*/ebin $(APPS_DIR)/*/ebin $(CURDIR)/ebin
ifdef t
ifeq (,$(findstring :,$(t)))
@@ -6214,17 +6390,17 @@ apps-eunit:
endif
endif
-# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2013-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-.PHONY: relx-rel distclean-relx-rel distclean-relx run
+.PHONY: relx-rel relx-relup distclean-relx-rel run
# Configuration.
-RELX ?= $(CURDIR)/relx
+RELX ?= $(ERLANG_MK_TMP)/relx
RELX_CONFIG ?= $(CURDIR)/relx.config
-RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.5.0/relx
+RELX_URL ?= https://github.com/erlware/relx/releases/download/v3.19.0/relx
RELX_OPTS ?=
RELX_OUTPUT_DIR ?= _rel
@@ -6239,10 +6415,12 @@ endif
ifeq ($(IS_DEP),)
ifneq ($(wildcard $(RELX_CONFIG)),)
rel:: relx-rel
+
+relup:: relx-relup
endif
endif
-distclean:: distclean-relx-rel distclean-relx
+distclean:: distclean-relx-rel
# Plugin-specific targets.
@@ -6251,14 +6429,14 @@ $(RELX):
$(verbose) chmod +x $(RELX)
relx-rel: $(RELX) rel-deps app
- $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS)
+ $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release tar
+
+relx-relup: $(RELX) rel-deps app
+ $(verbose) $(RELX) -c $(RELX_CONFIG) $(RELX_OPTS) release relup tar
distclean-relx-rel:
$(gen_verbose) rm -rf $(RELX_OUTPUT_DIR)
-distclean-relx:
- $(gen_verbose) rm -rf $(RELX)
-
# Run target.
ifeq ($(wildcard $(RELX_CONFIG)),)
@@ -6267,15 +6445,17 @@ else
define get_relx_release.erl
{ok, Config} = file:consult("$(RELX_CONFIG)"),
- {release, {Name, _}, _} = lists:keyfind(release, 1, Config),
- io:format("~s", [Name]),
+ {release, {Name, Vsn}, _} = lists:keyfind(release, 1, Config),
+ io:format("~s ~s", [Name, Vsn]),
halt(0).
endef
-RELX_RELEASE = `$(call erlang,$(get_relx_release.erl))`
+RELX_REL := $(shell $(call erlang,$(get_relx_release.erl)))
+RELX_REL_NAME := $(word 1,$(RELX_REL))
+RELX_REL_VSN := $(word 2,$(RELX_REL))
run: all
- $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_RELEASE)/bin/$(RELX_RELEASE) console
+ $(verbose) $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/bin/$(RELX_REL_NAME) console
help::
$(verbose) printf "%s\n" "" \
@@ -6284,8 +6464,8 @@ help::
endif
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# Copyright (c) 2014, M Robert Martin <[email protected]>
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
# This file is contributed to erlang.mk and subject to the terms of the ISC License.
.PHONY: shell
@@ -6315,10 +6495,9 @@ build-shell-deps: $(ALL_SHELL_DEPS_DIRS)
shell: build-shell-deps
$(gen_verbose) $(SHELL_ERL) -pa $(SHELL_PATHS) $(SHELL_OPTS)
-# Copyright (c) 2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Loïc Hoguin <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
-# @todo BUILD_DEPS too?
ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
.PHONY: triq
@@ -6327,7 +6506,7 @@ ifeq ($(filter triq,$(DEPS) $(TEST_DEPS)),triq)
tests:: triq
define triq_check.erl
- code:add_pathsa(["$(CURDIR)/ebin", "$(DEPS_DIR)/*/ebin"]),
+ code:add_pathsa(["$(call core_native_path,$(CURDIR)/ebin)", "$(call core_native_path,$(DEPS_DIR)/*/ebin)"]),
try
case $(1) of
all -> [true] =:= lists:usort([triq:check(M) || M <- [$(call comma_list,$(3))]]);
@@ -6359,6 +6538,7 @@ triq: test-build
endif
endif
+# Copyright (c) 2016, Loïc Hoguin <[email protected]>
# Copyright (c) 2015, Erlang Solutions Ltd.
# This file is part of erlang.mk and subject to the terms of the ISC License.
@@ -6367,9 +6547,9 @@ endif
# Configuration.
ifeq ($(XREF_CONFIG),)
- XREF_ARGS :=
+ XREFR_ARGS :=
else
- XREF_ARGS := -c $(XREF_CONFIG)
+ XREFR_ARGS := -c $(XREF_CONFIG)
endif
XREFR ?= $(CURDIR)/xrefr
@@ -6398,7 +6578,8 @@ xref: deps app $(XREFR)
distclean-xref:
$(gen_verbose) rm -rf $(XREFR)
-# Copyright 2015, Viktor Söderqvist <[email protected]>
+# Copyright (c) 2016, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015, Viktor Söderqvist <[email protected]>
# This file is part of erlang.mk and subject to the terms of the ISC License.
COVER_REPORT_DIR = cover
@@ -6493,7 +6674,8 @@ define cover_report.erl
true -> N - 1; false -> N end}} || {M, {Y, N}} <- Report],
TotalY = lists:sum([Y || {_, {Y, _}} <- Report1]),
TotalN = lists:sum([N || {_, {_, N}} <- Report1]),
- TotalPerc = round(100 * TotalY / (TotalY + TotalN)),
+ Perc = fun(Y, N) -> case Y + N of 0 -> 100; S -> round(100 * Y / S) end end,
+ TotalPerc = Perc(TotalY, TotalN),
{ok, F} = file:open("$(COVER_REPORT_DIR)/index.html", [write]),
io:format(F, "<!DOCTYPE html><html>~n"
"<head><meta charset=\"UTF-8\">~n"
@@ -6503,7 +6685,7 @@ define cover_report.erl
io:format(F, "<table><tr><th>Module</th><th>Coverage</th></tr>~n", []),
[io:format(F, "<tr><td><a href=\"~p.COVER.html\">~p</a></td>"
"<td>~p%</td></tr>~n",
- [M, M, round(100 * Y / (Y + N))]) || {M, {Y, N}} <- Report1],
+ [M, M, Perc(Y, N)]) || {M, {Y, N}} <- Report1],
How = "$(subst $(space),$(comma)$(space),$(basename $(COVERDATA)))",
Date = "$(shell date -u "+%Y-%m-%dT%H:%M:%SZ")",
io:format(F, "</table>~n"
@@ -6518,3 +6700,156 @@ cover-report:
endif
endif # ifneq ($(COVER_REPORT_DIR),)
+
+# Copyright (c) 2016, Loïc Hoguin <[email protected]>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+.PHONY: sfx
+
+ifdef RELX_REL
+ifdef SFX
+
+# Configuration.
+
+SFX_ARCHIVE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME)/$(RELX_REL_NAME)-$(RELX_REL_VSN).tar.gz
+SFX_OUTPUT_FILE ?= $(RELX_OUTPUT_DIR)/$(RELX_REL_NAME).run
+
+# Core targets.
+
+rel:: sfx
+
+# Plugin-specific targets.
+
+define sfx_stub
+#!/bin/sh
+
+TMPDIR=`mktemp -d`
+ARCHIVE=`awk '/^__ARCHIVE_BELOW__$$/ {print NR + 1; exit 0;}' $$0`
+FILENAME=$$(basename $$0)
+REL=$${FILENAME%.*}
+
+tail -n+$$ARCHIVE $$0 | tar -xzf - -C $$TMPDIR
+
+$$TMPDIR/bin/$$REL console
+RET=$$?
+
+rm -rf $$TMPDIR
+
+exit $$RET
+
+__ARCHIVE_BELOW__
+endef
+
+sfx:
+ $(call render_template,sfx_stub,$(SFX_OUTPUT_FILE))
+ $(gen_verbose) cat $(SFX_ARCHIVE) >> $(SFX_OUTPUT_FILE)
+ $(verbose) chmod +x $(SFX_OUTPUT_FILE)
+
+endif
+endif
+
+# Copyright (c) 2013-2015, Loïc Hoguin <[email protected]>
+# Copyright (c) 2015-2016, Jean-Sébastien Pédron <[email protected]>
+# This file is part of erlang.mk and subject to the terms of the ISC License.
+
+# Fetch dependencies recursively (without building them).
+
+.PHONY: fetch-deps fetch-doc-deps fetch-rel-deps fetch-test-deps \
+ fetch-shell-deps
+
+.PHONY: $(ERLANG_MK_RECURSIVE_DEPS_LIST) \
+ $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
+ $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
+ $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
+ $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
+
+fetch-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
+fetch-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
+fetch-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
+fetch-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
+fetch-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
+
+ifneq ($(SKIP_DEPS),)
+$(ERLANG_MK_RECURSIVE_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
+ $(verbose) :> $@
+else
+# By default, we fetch "normal" dependencies. They are also included no
+# matter the type of requested dependencies.
+#
+# $(ALL_DEPS_DIRS) includes $(BUILD_DEPS).
+
+$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DEPS_DIRS)
+$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_DOC_DEPS_DIRS)
+$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_REL_DEPS_DIRS)
+$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_TEST_DEPS_DIRS)
+$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST): $(ALL_DEPS_DIRS) $(ALL_SHELL_DEPS_DIRS)
+
+# Allow to use fetch-deps and $(DEP_TYPES) to fetch multiple types of
+# dependencies with a single target.
+ifneq ($(filter doc,$(DEP_TYPES)),)
+$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_DOC_DEPS_DIRS)
+endif
+ifneq ($(filter rel,$(DEP_TYPES)),)
+$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_REL_DEPS_DIRS)
+endif
+ifneq ($(filter test,$(DEP_TYPES)),)
+$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_TEST_DEPS_DIRS)
+endif
+ifneq ($(filter shell,$(DEP_TYPES)),)
+$(ERLANG_MK_RECURSIVE_DEPS_LIST): $(ALL_SHELL_DEPS_DIRS)
+endif
+
+ERLANG_MK_RECURSIVE_TMP_LIST := $(abspath $(ERLANG_MK_TMP)/recursive-tmp-deps.log)
+
+$(ERLANG_MK_RECURSIVE_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_REL_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \
+$(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST):
+ifeq ($(IS_APP)$(IS_DEP),)
+ $(verbose) mkdir -p $(ERLANG_MK_TMP)
+ $(verbose) rm -f $(ERLANG_MK_RECURSIVE_TMP_LIST)
+endif
+ifndef IS_APP
+ $(verbose) for dep in $(ALL_APPS_DIRS) ; do \
+ $(MAKE) -C $$dep $@ \
+ IS_APP=1 \
+ ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
+ || exit $$?; \
+ done
+endif
+ $(verbose) for dep in $^ ; do \
+ if ! grep -qs ^$$dep$$ $(ERLANG_MK_RECURSIVE_TMP_LIST); then \
+ echo $$dep >> $(ERLANG_MK_RECURSIVE_TMP_LIST); \
+ if grep -qs -E "^[[:blank:]]*include[[:blank:]]+(erlang\.mk|.*/erlang\.mk)$$" \
+ $$dep/GNUmakefile $$dep/makefile $$dep/Makefile; then \
+ $(MAKE) -C $$dep fetch-deps \
+ IS_DEP=1 \
+ ERLANG_MK_RECURSIVE_TMP_LIST=$(ERLANG_MK_RECURSIVE_TMP_LIST) \
+ || exit $$?; \
+ fi \
+ fi \
+ done
+ifeq ($(IS_APP)$(IS_DEP),)
+ $(verbose) sort < $(ERLANG_MK_RECURSIVE_TMP_LIST) | uniq > $@
+ $(verbose) rm $(ERLANG_MK_RECURSIVE_TMP_LIST)
+endif
+endif # ifneq ($(SKIP_DEPS),)
+
+# List dependencies recursively.
+
+.PHONY: list-deps list-doc-deps list-rel-deps list-test-deps \
+ list-shell-deps
+
+list-deps: $(ERLANG_MK_RECURSIVE_DEPS_LIST)
+list-doc-deps: $(ERLANG_MK_RECURSIVE_DOC_DEPS_LIST)
+list-rel-deps: $(ERLANG_MK_RECURSIVE_REL_DEPS_LIST)
+list-test-deps: $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST)
+list-shell-deps: $(ERLANG_MK_RECURSIVE_SHELL_DEPS_LIST)
+
+list-deps list-doc-deps list-rel-deps list-test-deps list-shell-deps:
+ $(verbose) cat $^
diff --git a/rebar.config b/rebar.config
index 6fbd3d8..899a7c8 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,2 +1,4 @@
-{deps, [{cowlib,".*",{git,"https://github.com/ninenines/cowlib","master"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.2.1"}}]}.
+{deps, [
+{cowlib,".*",{git,"https://github.com/ninenines/cowlib","master"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.2.1"}}
+]}.
{erl_opts, [debug_info,warn_export_vars,warn_shadow_vars,warn_obsolete_guard,warn_export_all,warn_missing_spec,warn_untyped_record]}.