From faca7866ed1c59cd41e11fcfc5a9f95378380497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sat, 5 Nov 2016 14:17:30 +0200 Subject: Partially update manual for the cowboy_req Only the access functions have been modified so far. --- Makefile | 2 + doc/src/manual/cowboy_req.asciidoc | 793 +++++---------------------- doc/src/manual/cowboy_req.header.asciidoc | 74 +++ doc/src/manual/cowboy_req.headers.asciidoc | 51 ++ doc/src/manual/cowboy_req.host.asciidoc | 52 ++ doc/src/manual/cowboy_req.method.asciidoc | 60 +++ doc/src/manual/cowboy_req.path.asciidoc | 51 ++ doc/src/manual/cowboy_req.peer.asciidoc | 61 +++ doc/src/manual/cowboy_req.port.asciidoc | 52 ++ doc/src/manual/cowboy_req.qs.asciidoc | 50 ++ doc/src/manual/cowboy_req.scheme.asciidoc | 55 ++ doc/src/manual/cowboy_req.uri.asciidoc | 116 ++++ doc/src/manual/cowboy_req.version.asciidoc | 49 ++ ebin/cowboy.app | 5 +- erlang.mk | 829 ++++++++++++++++++++--------- rebar.config | 4 +- 16 files changed, 1409 insertions(+), 895 deletions(-) create mode 100644 doc/src/manual/cowboy_req.header.asciidoc create mode 100644 doc/src/manual/cowboy_req.headers.asciidoc create mode 100644 doc/src/manual/cowboy_req.host.asciidoc create mode 100644 doc/src/manual/cowboy_req.method.asciidoc create mode 100644 doc/src/manual/cowboy_req.path.asciidoc create mode 100644 doc/src/manual/cowboy_req.peer.asciidoc create mode 100644 doc/src/manual/cowboy_req.port.asciidoc create mode 100644 doc/src/manual/cowboy_req.qs.asciidoc create mode 100644 doc/src/manual/cowboy_req.scheme.asciidoc create mode 100644 doc/src/manual/cowboy_req.uri.asciidoc create mode 100644 doc/src/manual/cowboy_req.version.asciidoc 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 +# Copyright (c) 2013-2016, Loïc Hoguin # # 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 " \ + "Copyright (c) 2013-2016 Loïc Hoguin " \ "" \ "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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 # Copyright (c) 2015, Viktor Söderqvist # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2014-2016, Loïc Hoguin # 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 +# Copyright (c) 2014-2016, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2014, Dave Cottlehuber # 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 # Copyright (c) 2014, Enrique Fernandez -# Copyright (c) 2015, Loïc Hoguin # 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 +# Copyright (c) 2013-2016, Loïc Hoguin # 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 # Copyright (c) 2014, M Robert Martin -# Copyright (c) 2015, Loïc Hoguin # 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 +# Copyright (c) 2015-2016, Loïc Hoguin # 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 # 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 +# Copyright (c) 2016, Loïc Hoguin +# Copyright (c) 2015, Viktor Söderqvist # 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, "~n" "~n" @@ -6503,7 +6685,7 @@ define cover_report.erl io:format(F, "~n", []), [io:format(F, "" "~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, "
ModuleCoverage
~p~p%
~n" @@ -6518,3 +6700,156 @@ cover-report: endif endif # ifneq ($(COVER_REPORT_DIR),) + +# Copyright (c) 2016, Loïc Hoguin +# 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 +# Copyright (c) 2015-2016, Jean-Sébastien Pédron +# 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]}. -- cgit v1.2.3