Skip to content
This repository

Use Rebar to get, update and compile dependencies (and only dependencies) #503

Closed
wants to merge 15 commits into from

6 participants

amiramix Arjan Scherpenisse Andreas Stenius Maas-Maarten Zeeman Marc Worrell mgpld
amiramix

Zotonic is still compiled with the good old method using Emakefile

This Pull Request contains the minimal amount of changes to leverage Rebar dependency management without changing the compilation process of Zotonic itself.

Rationale:

When Zotonic is used as a part of a bigger application, the internal Zotonic dependencies (in Zotonic's deps folder) can clash with dependencies of the main application. Imagine the following folder structure:

main_app/deps/cowboy
main_app/deps/lager
main_app/deps/charreada
main_app/deps/zotonic/deps/cowboy
main_app/deps/zotonic/deps/lager
etc...

In this case cowboy (if used by Zotonic) and lager would be compiled and started twice from different paths.

Also, all Zotonic dependencies use Rebar and Rebar manages its own dependencies pretty well. For example Ranch is listed as dependency inside Cowboy, and both Cobwoy and Ranch are listed as dependencies inside Charrreada. When downloading dependencies with Rebar it will download Ranch and Cowboy only once to the main_app/deps folder (and NOT to local deps folders inside Cowboy or Charreada). It means everything is much simplified because all dependencies, no matter how complex the main app is, are only downloaded once into main_app/deps folder, and not to deps subfolders of the dependencies subfolders...

There is no reason to not leverage this simplicity when using Zotonic.

But on the other hand we don't want to compile Zotonic with Rebar because Emakefile is much quicker and is also used from z:m() (which wouldn't work when compiling Zotonic with Rebar).

Solution:

Add rebar.config and amend GNUmakefile so that all dependencies are managed by Rebar, but leave compiling Zotonic with the old method. The GNUmakefile has been simplified to contain a simple set of targets:

get-deps: Use Rebar to download all missing Zotonic dependencies; the old method with the use of git submodules works too - git submodules simply don't have to be used when Zotonic is compiled as a part of a bigger application

update-deps: Use Rebar to update dependencies

compile-deps: Use Rebar to compile dependencies, but only those which are present in Zotonic/deps sub-folder. If Rebar resolved Zotonic dependencies so that they are downloaded in the main_app/deps subfolder instead of main_app/deps/zotonic/deps then they won't be compiled by Zotonic's makefile but by the main_app's makefile !!

compile-zotonic: Old rules to compile Zotonic using Emakefile

all: compile-deps + compile-zotonic

docs, clean, clean_logs work as previously.

Please let me know if you have any questions or suggestions regarding this change.

amiramix amiramix Use Rebar to get, update and compile dependencies (only dependencies …
…- Zotonic is still compiled with the good old method using Emakefile)
6f3a8c3
Arjan Scherpenisse
Owner

Nice change! I would even consider removing the git submodules entirely. IMHO they do more harm than good.

The only downside I see to using rebar is that it adds git as a build-time dependency. Any thoughts on how to prevent that?

Andreas Stenius
Owner

Well, the git dependency is only for fetching the deps using the git:// protocol, right.

And, building from master (i.e. a git clone'd source tree), that should be fine.
For bundled source builds (i.e. downloaded tar balls) however, we could instead have deps pointing to http:// sources. That way it shouldn't need git to fetch them.

So, we would need some way to easily manage the rebar config, pointing to git deps from a dev repo, and to http deps on the release branch. Doable?

Arjan Scherpenisse
Owner

How do such HTTP deps look like in a rebar config?
because if you just change git:// to http:// it will still use git to clone the http:// one.

And we have the added issue that sub-dependencies might also depend on git being installed.

Andreas Stenius
Owner

Ah, maybe that is a shortcoming of rebar then. I didn't look into it, just thinking it should work. (will look into it now...)

About deps having git deps.. maybe we need to host the http based deps, and make sure they are "release" friendly..
I think a tool is needed to ensure this. I envision a rebar extension, that can walk through the git deps, and fetch them into a output dir, along with modified rebar.config files that converts git to http in the process..
That way, it should be a simple command to update the dev rebar.config to a release one without any git in it.

Arjan Scherpenisse
Owner

I like that idea. zutils already has a z-archive command that does the above for git submodules. But I prefer using rebar, actually.

If we create a zip bundle like that, we can just list the dependencies in the rebar config without any source URL like this:

{deps, [lager, mochiweb, z_stdlib]}.

That will get them to build but does not assume anything about their source.

Arjan

Andreas Stenius
Owner

Ah, even better. :+1:

amiramix

I've created a simple escript command that reads rebar.config and extracts all Application names. That could be used to create a rebar.config file to use when doing a release.

However I have to say I don't quite see what is the problem. Unless someone specifically issues the command 'rebar get-deps' or 'rebar update-deps' rebar is not going to use the list of dependencies nor it is going to download anything using git or any other tool. Issuing just 'rebar compile' in a submodule doesn't cause rebar to download anything.

Andreas Stenius
Owner

Nice :)

But I can't not hint at http://zotonic.com/docs/0.9/dev/codestyle.html#writing-commit-messages when Arjan wields it at pull requests :p

Collaborator

:+1: Thanks for the reminder. This was definitely core, and nothing was fixed. :-)

Owner

I dont understand this commit. What do you mean with "garbage being generated"?

Collaborator

filename:join also normalizes the filenames. Which leads to a lot of unneeded work when it recursively descends, a potentially large, directory tree. We just need the join, not the normalization. The name is also not concatenated with ++. This would copy BaseDir n times in a new list, but instead it places it in a small new list without copying it.

Owner

Maybe make our own simple join in z_stdlib?
In that way we can always easily join directories to filenames.

Note that in R14B and later the input can be a list() or binary() (I remember vaguely that it even accepts atoms)

kaos and others added some commits
Andreas Stenius kaos doc: Add `build:` tag to list of prefixes. 02560b7
Andreas Stenius kaos doc: Add `translations:` tag to list of prefixes. 3a865bd
Andreas Stenius kaos doc: rename `translations:` to `translation:`. 2b586c4
mgpld mgpld mod_base: Modal html title in z.dialog
Modal title was rendered as text element.
The problem was that htmlentities wasn't parsed at all:
i.e. é -> é instead of é
bbf2881
Arjan Scherpenisse arjan core: Keep file extension when it is allowed for the file's mime type
This fixes a bug where an .mp3 file was renamed to .m2a on upload,
because mimetypes returns multiple extensions for the mime type
audio/mpeg. Instead of taking the first extension found, it checks
whether the original filename has already an extension that is
allowed.
f301dd3
amiramix

Please let me know if you require any changes to my pull-request needed before integrating it or if you have any questions or doubts.

Arjan Scherpenisse
Owner

Two things:

1) Remove the git submodules because that is the same as using rebar deps

2) Make "rebar compile" return an error message (or let it call "make"); if ppl see a rebar.config they expect to compile it with rebar as well.

After that this is good to merge imho :) :+1:

amiramix

Any idea how I can intercept "rebar compile" to not let it compile Zotonic? I looked a little bit around but couldn't find anything about it. I don't think it's possible. It's another limitation of rebar I guess. I may ask on the rebar forum though, I heard there is one.

amiramix

On a second thought, rebar doesn't do anything special during compilation and as long as Zotonic follows OTP there shouldn't be any reason why ppl shouldn't want to compile Zotonic with Rebar. I know it currently it doesn't compile because some of the includes it can't find, but maybe the solution is to make Zotonic compilable by Rebar as an option if someone really wants to?

amiramix

Another problem is that when zotonic is itself a dependency of another application, rebar refuses to compile Zotonic (by doing e.g. cd deps/zotonic && rebar compile) because it can't find the dependencies. It can't, of course, because they have been already downloaded as dependencies of the main project, not Zotonic dependencies. There is no such concept like compile just one dependency in Rebar.

I have to say that Rebar sucks. It may be fine for small projects but to use it with a bigger projects with multiple dependencies it is pain in the ... I am glad Zotonic doesn't depend on Rebar.

Arjan Scherpenisse
Owner

Heheh. But do you still agree with the approach for this pull request then?

amiramix

Well, yes, because since all dependencies use Rebar I still think Rebar should be used to manage them. I just don't like Rebar's approach - if you use me to compile one project then it's best for you if you use me for all the other your projects too. It's like cancer. Everything should be rebarized or nothing.

In this case the problem I guess is with Dependencies of Zotonic dependencies. If I have a bigger application with many dependencies, Zotonic being one of them, and those apps may have their own dependencies, then when I want to create a deployment I'd like to have all those dependencies in a single folder, so they are not duplicated (as I stated originally). If all those dependencies use Rebar why don't use it to manage them then?

Maas-Maarten Zeeman
Collaborator

Recently found that out too… I wanted to continue development so I worked around rebar. Very annoying.

GNUmakefile
@@ -17,18 +28,31 @@ erl:
17 28 $(PARSER).erl: $(PARSER).yrl
18 29 $(ERLC) -o src/erlydtl $(PARSER).yrl
19 30
20   -lager:
21   - cd deps/lager && ./rebar compile
  31 +ebin/$(APP).app: src/$(APP).app.src
  32 + cp src/$(APP).app.src $@
  33 +
  34 +# Phony rule to compile a particular dependency with Rebar
  35 +compile-rule/%: $(REBAR)
  36 + cd $* && $(REBAR) compile
2
Andreas Stenius Owner
kaos added a note

I see an issue here, if REBAR points to ./rebar and the dep doesn't have rebar itself, it will break. Better is to define REBAR with an absolute path, that way the dep doesn't have to have rebar itself.

amiramix
amiramix added a note

You are right, thanks, will be posting a fix shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Maas-Maarten Zeeman
Collaborator

I solved it by calling rebar for each sub-project with a fixed deps dir and handle all the deps from a rebar config in the main project. What happens then is that rebar compiles multi deps, like z_stdlib, multiple times.

What I also found odd, from a security standpoint, is that each of those project ship with binary rebar executables.

Andreas Stenius
Owner

OK, so rebar doesn't scale well. I think that zotonic's current setup doesn't scale well either (as in having zotonic as a dependency in a larger setup).
With that in mind, trying to solve the latter might solve the former as well (not solve per se, but get rid of the need).

So, just brainstorming here, one way to make zotonic fit in a bigger picture would be to defer the dependency parts for zotonic. That would leave us with a split of the current zotonic into a zotonic-lib, and a zotonic-app. The zotonic-lib has all the functionality, and zotonic-app simply ties the whole thing together with the required deps and all. So if zotonic is to be used as a dep in another project, they can point to zotonic-lib, and take care of the needed deps them selves.

Especially if all deps are OTP apps, it should be a breeze.

Just to clarify my thoughts (in case my writing was a bit fuzzy):

ERL_LIBS/
   lager-1.0.0/
   bert-master/
   zstdlib-master/
   zotonic_app-0.9.1/    <-- simple app just to kick start the zotonic_lib
   zotonic_lib-0.9.1/      <-- holds all code, modules etc that is zotonic today 
                     except the deps; it only lists the required deps in the .app file
   otherstuff-x.y.z/

Edit:
Ah, missed to say, that it would be up to the zotonic_app to make sure we have and build the required deps.

amiramix

@kaos
I think it depends how you define a dependency for Zotonic. Zotonic itself follows the OTP principle in having the src, ebin, priv and include libraries. I think what's spoiling the picture are all the other folders like models, modules, controllers, etc. I am not sure if they break OTP. Maybe it would be enough to just move them under the src directory?

Also, I am not sure what you mean by zotonic-lib. Zotonic must be itself as an app. In OTP when another project depends on Zotonic it just lists it as one of the dependencies, e.g. {applications, [kernel, stdlib, cowboy, ibrowse]}. Then OTP starts those apps automatically, in case of Zotonic by calling zotonic:start().

Zotonic can be listed in the release file too:

{release,
[ {kernel, (...)
,{compiler, "4.8.2"}
,{syntax_tools, "1.6.9"}
,{lager, "2.0.0"}
,{webzmachine, "1.8.1"}
,{zotonic, "0.10-dev"}
]}.

I don't quite see how you would like to separate zotonic modules internally into app and lib parts.

amiramix

@mmzeeman
I understand your approach, but for that you would need to change the zotonic's rebar.config (to specify a custom deps folder)?

Maas-Maarten Zeeman
Collaborator
Andreas Stenius
Owner

@amiramix
I felt my post would be confusing. I'll try to elaborate my idea a bit, see if it helps :p

First, the zotonic-lib name is a bit misleading, as it is meant to be an OTP app as well.
The modules dir need to move, I think, to be fully OTP-compliant, but that is beside the point.

My discussion was focusing on getting rid of the deps dir. The dependencies can still be listed in the .app file,
but when building zotonic-lib, all dependencies need to be met (and built) already. This is the job for zotonic-app,
which itself is another OTP app. It has a single dependency (in the .app file), namely the zotonic-lib. However, when building zotonic-app, it makes sure that the deps needed by zotonic-lib are also present, and built.

So, at build time, zotonic-app manages the deps, and at runtime zotonic-lib ensures they are all started (by listing them in it's .app file).

This way, in case zotonic is to be used as "just another dep" in a larger app that already has all (or some of) the deps needed by zotonic, already with its own build system, it can just drop in zotonic-lib as another dep and leave zotonic-app out of the way. In case not all zotonic deps were already met, it needs to add those, of course, and can peek at the deps pulled in by zotonic-app to find out which deps are needed (and which versions etc).

It's getting late here, and maybe it's not such an great idea, so I'll stop typing now.. ;)

amiramix

I see, but this is exactly why I did this change that is in this pull request. Basically, when Rebar is used to manage Zotonic dependencies it downloads all dependency dependencies into one place. I tried to explain that in my original comment. There is no need to split Zotonic up into separate lib and app applications.

So, by example, this is rebar.config of the main project:

{deps,
 [{charreada, ".*", {git, "git://github.com/yoonka/charreada.git", "master"}},
  {zotonic, ".*", {git, "git://github.com/zotonic/zotonic.git", "master"}}]}.

Now, when I do rebar list-deps I get:

==> Entering directory `/usr/home/g/main/project/deps/charreada'
==> Entering directory `/usr/home/g/main/project/deps/cowboy'
==> Entering directory `/usr/home/g/main/project/deps/ranch'
==> ranch (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/ranch'
==> cowboy (list-deps)
ranch REV 0.6.0 git://github.com/extend/ranch.git
==> Leaving directory `/usr/home/g/main/project/deps/cowboy'
==> Entering directory `/usr/home/g/main/project/deps/ibrowse'
==> ibrowse (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/ibrowse'
==> charreada (list-deps)
cowboy REV master git://github.com/extend/cowboy.git
ibrowse REV master git://github.com/cmullaparthi/ibrowse.git
==> Leaving directory `/usr/home/g/main/project/deps/charreada'
==> Entering directory `/usr/home/g/main/project/deps/zotonic'
==> Entering directory `/usr/home/g/main/project/deps/bert'
==> bert (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/bert'
==> Entering directory `/usr/home/g/main/project/deps/dh_date'
==> dh_date (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/dh_date'
==> Entering directory `/usr/home/g/main/project/deps/gen_smtp'
==> gen_smtp (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/gen_smtp'
==> Entering directory `/usr/home/g/main/project/deps/lager'
==> lager (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/lager'
==> Entering directory `/usr/home/g/main/project/deps/mimetypes'
==> mimetypes (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/mimetypes'
==> Entering directory `/usr/home/g/main/project/deps/ua_classifier'
==> ua_classifier (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/ua_classifier'
==> Entering directory `/usr/home/g/main/project/deps/webzmachine'
==> webzmachine (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/webzmachine'
==> Entering directory `/usr/home/g/main/project/deps/z_stdlib'
==> z_stdlib (list-deps)
==> Leaving directory `/usr/home/g/main/project/deps/z_stdlib'
==> zotonic (list-deps)
bert REV master git://github.com/zotonic/bert.erl.git
dh_date REV master git://github.com/zotonic/dh_date.git
gen_smtp REV master git://github.com/zotonic/gen_smtp.git
lager TAG 1.0.0 git://github.com/basho/lager.git
mimetypes REV master git://github.com/zotonic/mimetypes.git
ua_classifier REV master git://github.com/zotonic/ua_classifier.git
webzmachine REV master git://github.com/zotonic/webzmachine.git
z_stdlib REV master git://github.com/zotonic/z_stdlib.git
==> Leaving directory `/usr/home/g/main/project/deps/zotonic'
==> mainproject (list-deps)
charreada REV master git://github.com/yoonka/charreada.git
zotonic REV master git://github.com/zotonic/zotonic.git

As you see, it lists all dependencies of all the dependencies, including Zotonic. When I do rebar get-deps it downloads all of them into mainproject/deps leaving Zotonic/deps empty (apart from iconv and mochiweb). I wrote the new Zotonic makefile in such a way that it compiles only what is present in Zotonic/deps so if Rebar downloaded Zotonic dependencies into the main deps folder they won't be compiled (they will be compiled by the makefile of the main project).

Hope that makes sense?

Andreas Stenius
Owner

Ah, nice!

I didn't get that part from your initial comment :)

Definitely a step in the right direction then :+1:

amiramix

I assumed that everyone who is using rebar already knows about this feature and I am the last one to learn that it works like that, as I myself discovered it only a couple of weeks ago :)

Anything else is needed in my pull request before considering integrating it into Zotonic project?

Andreas Stenius
Owner
kaos commented

Replaced by #515.

Andreas Stenius kaos closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 15 unique commits by 6 authors.

Jan 21, 2013
amiramix amiramix Use Rebar to get, update and compile dependencies (only dependencies …
…- Zotonic is still compiled with the good old method using Emakefile)
6f3a8c3
Jan 22, 2013
Marc Worrell mworrell core: allow an included file to use extends/overrules within the scop…
…e of the included file.
bbcc9d8
Jan 23, 2013
Marc Worrell mworrell mod_survey: fix for yes/no and true/false questions with immediate su…
…bmit.
97ab471
Maas-Maarten Zeeman mmzeeman Module indexing optimizations. 4e3e2f0
Jan 24, 2013
Maas-Maarten Zeeman mmzeeman Prevent the usage of filename:join, we can assume filenames are ok, b…
…ecause they come from the system. This prevents a lot of garbage being generated.
6aae557
Andreas Stenius kaos doc: Add `build:` tag to list of prefixes. 02560b7
Andreas Stenius kaos doc: Add `translations:` tag to list of prefixes. 3a865bd
Andreas Stenius kaos doc: rename `translations:` to `translation:`. 2b586c4
mgpld mgpld mod_base: Modal html title in z.dialog
Modal title was rendered as text element.
The problem was that htmlentities wasn't parsed at all:
i.e. &eacute; -> &eacute; instead of é
bbf2881
Arjan Scherpenisse arjan core: Keep file extension when it is allowed for the file's mime type
This fixes a bug where an .mp3 file was renamed to .m2a on upload,
because mimetypes returns multiple extensions for the mime type
audio/mpeg. Instead of taking the first extension found, it checks
whether the original filename has already an extension that is
allowed.
f301dd3
amiramix amiramix Delete git submodules and allow rebar to compile the main Zotonic pro…
…ject
8a85abc
amiramix amiramix Allow building dependencies with locally downloaded Rebar 94cdd2b
Jan 25, 2013
amiramix amiramix Fix relative lib includes so that Zotonic can be compiled with deps n…
…ot in the default folder
a5fcb70
amiramix amiramix Update controller_static_pages documentation 1cf5929
Feb 02, 2013
amiramix amiramix Merge branch 'manage-dependencies-with-rebar' into static-pages-update 60a2ab9
This page is out of date. Refresh to see the latest.

Showing 29 changed files with 203 additions and 105 deletions. Show diff stats Hide diff stats

  1. +10 0 .gitignore
  2. +0 24 .gitmodules
  3. +1 0  Emakefile
  4. +47 19 GNUmakefile
  5. +0 1  deps/bert.erl
  6. +0 1  deps/dh_date
  7. +0 1  deps/gen_smtp
  8. +0 1  deps/lager
  9. +0 1  deps/mimetypes
  10. +0 1  deps/ua_classifier
  11. +0 1  deps/webzmachine
  12. +0 1  deps/z_stdlib
  13. +6 4 doc/dev/codestyle.rst
  14. +1 1  doc/ref/controllers/controller_static_pages.rst
  15. +1 1  include/zotonic.hrl
  16. +1 1  modules/mod_base/lib/js/modules/z.dialog.js
  17. +1 1  modules/mod_development/controllers/controller_wmtrace.erl
  18. +1 1  modules/mod_development/controllers/controller_wmtrace_conf.erl
  19. +3 3 modules/mod_survey/templates/blocks/_block_view_survey_truefalse.tpl
  20. +2 2 modules/mod_survey/templates/blocks/_block_view_survey_yesno.tpl
  21. +41 0 rebar.config
  22. +2 1  src/erlydtl/erlydtl_compiler.erl
  23. +2 1  src/models/m_media.erl
  24. +36 5 src/support/z_media_identify.erl
  25. +20 17 src/support/z_module_indexer.erl
  26. +10 2 src/support/z_module_manager.erl
  27. +1 1  src/support/z_tracer.erl
  28. +17 13 src/support/z_utils.erl
  29. 0  src/{zotonic.app → zotonic.app.src}
10 .gitignore
@@ -2,6 +2,7 @@
2 2 zotonic.pid
3 3 zotonic.tmproj
4 4 erl_crash.dump
  5 +rebar
5 6
6 7 *~
7 8
@@ -22,6 +23,15 @@ ebin/
22 23 /deps/mochiweb/doc/
23 24 /deps/mochiweb/.eunit/
24 25
  26 +/deps/bert/
  27 +/deps/dh_date/
  28 +/deps/gen_smtp/
  29 +/deps/lager/
  30 +/deps/mimetypes/
  31 +/deps/ua_classifier/
  32 +/deps/webzmachine/
  33 +/deps/z_stdlib/
  34 +
25 35 /src/erlydtl/erlydtl_parser.erl
26 36 /src/tests/erlydtl/rendered_output/
27 37
24 .gitmodules
... ... @@ -1,24 +0,0 @@
1   -[submodule "deps/webzmachine"]
2   - path = deps/webzmachine
3   - url = git://github.com/zotonic/webzmachine.git
4   -[submodule "deps/gen_smtp"]
5   - path = deps/gen_smtp
6   - url = git://github.com/zotonic/gen_smtp.git
7   -[submodule "deps/lager"]
8   - path = deps/lager
9   - url = git://github.com/basho/lager.git
10   -[submodule "deps/ua_classifier"]
11   - path = deps/ua_classifier
12   - url = git://github.com/zotonic/ua_classifier.git
13   -[submodule "deps/dh_date"]
14   - path = deps/dh_date
15   - url = git://github.com/zotonic/dh_date.git
16   -[submodule "deps/mimetypes"]
17   - path = deps/mimetypes
18   - url = git://github.com/zotonic/mimetypes.git
19   -[submodule "deps/bert.erl"]
20   - path = deps/bert.erl
21   - url = git://github.com/zotonic/bert.erl.git
22   -[submodule "deps/z_stdlib"]
23   - path = deps/z_stdlib
24   - url = git://github.com/zotonic/z_stdlib.git
1  Emakefile
@@ -7,6 +7,7 @@
7 7 "src/*/*/*/*",
8 8 "modules/*/*",
9 9 "modules/*/*/*",
  10 + "modules/*/deps/*/src/*",
10 11 "priv/modules/*/*",
11 12 "priv/modules/*/*/*",
12 13 "priv/extensions/ext_*/*",
66 GNUmakefile
... ... @@ -1,14 +1,26 @@
1 1 ERL ?= erl
2 2 ERLC ?= $(ERL)c
3 3 APP := zotonic
4   -PARSER =src/erlydtl/erlydtl_parser
  4 +PARSER =src/erlydtl/erlydtl_parser
5 5
6   -GIT_CHECK := $(shell test -d .git && git submodule update --init)
7   -MAKEFILES := $(shell find -L deps modules priv/sites priv/modules priv/extensions priv/sites/*/modules -maxdepth 2 -name Makefile)
  6 +# List existing dependencies eventually putting lager first (if exists)
  7 +_DEPS_DIRS =$(filter-out deps/lager/, $(filter %/, $(wildcard deps/*/)))
  8 +_DEPS =$(_DEPS_DIRS:%/=%)
  9 +_WITH_LAGER =$(wildcard deps/lager) $(_DEPS)
  10 +DEPS_CMD =$(foreach dep, $(_WITH_LAGER), compile-rule/$(dep))
8 11
9   -.PHONY: all
10   -all: lager iconv mimetypes makefile-deps $(PARSER).erl erl ebin/$(APP).app
  12 +# Erlang Rebar downloading, see: https://groups.google.com/forum/?fromgroups=#!topic/erlang-programming/U0JJ3SeUv5Y
  13 +REBAR=$(shell which rebar || echo ./rebar)
  14 +REBAR_DEPS=$(shell which rebar || echo ../../rebar)
  15 +REBAR_URL=http://cloud.github.com/downloads/basho/rebar/rebar
  16 +
  17 +./rebar:
  18 + $(ERL) -noshell -s inets -s ssl \
  19 + -eval 'httpc:request(get, {"$(REBAR_URL)", []}, [], [{stream, "./rebar"}])' \
  20 + -s init stop
  21 + chmod +x ./rebar
11 22
  23 +# Helper targets
12 24 .PHONY: erl
13 25 erl:
14 26 @$(ERL) -pa $(wildcard deps/*/ebin) -pa ebin -noinput +B \
@@ -17,18 +29,33 @@ erl:
17 29 $(PARSER).erl: $(PARSER).yrl
18 30 $(ERLC) -o src/erlydtl $(PARSER).yrl
19 31
20   -lager:
21   - cd deps/lager && ./rebar compile
  32 +ebin/$(APP).app: src/$(APP).app.src
  33 + cp src/$(APP).app.src $@
  34 +
  35 +# Phony rule to compile a particular dependency with Rebar
  36 +compile-rule/%: $(REBAR)
  37 + cd $* && $(REBAR_DEPS) compile
  38 +
  39 +# Use Rebar to get, update and compile dependencies
  40 +.PHONY: get-deps update-deps compile-deps compile-zotonic compile
22 41
23   -iconv:
24   - cd deps/iconv && ./rebar compile
  42 +get-deps: $(REBAR)
  43 + $(REBAR) get-deps
25 44
26   -mimetypes:
27   - cd deps/mimetypes && ./rebar compile
  45 +update-deps: $(REBAR)
  46 + $(REBAR) update-deps
28 47
29   -makefile-deps:
30   - @if [ "${MAKEFILES}" != "" ]; then for f in ${MAKEFILES}; do echo $$f; $(MAKE) -C `dirname $$f`; done; fi
  48 +compile-deps: $(REBAR) $(DEPS_CMD)
  49 +
  50 +compile-zotonic: $(PARSER).erl erl ebin/$(APP).app
  51 +
  52 +compile: all
  53 +
  54 +# Default target - call all compile rules in succession
  55 +.PHONY: all
  56 +all: compile-deps compile-zotonic
31 57
  58 +# Generate documentation
32 59 .PHONY: docs edocs
33 60 docs:
34 61 @echo Building HTML documentation...
@@ -39,6 +66,7 @@ edocs:
39 66 @echo Building reference edoc documentation...
40 67 bin/zotonic generate-edoc
41 68
  69 +# Cleaning
42 70 .PHONY: clean_logs
43 71 clean_logs:
44 72 @echo "deleting logs:"
@@ -46,12 +74,12 @@ clean_logs:
46 74 rm -f priv/log/*
47 75
48 76 .PHONY: clean
49   -clean: clean_logs
  77 +clean: clean_logs $(REBAR)
50 78 @echo "removing:"
51   - (cd deps/iconv; ./rebar clean)
52   - (cd deps/mimetypes; ./rebar clean)
53   - @if [ "${MAKEFILES}" != "" ]; then for f in ${MAKEFILES}; do echo $$f; $(MAKE) -C `dirname $$f` clean; done; fi
54 79 rm -f ebin/*.beam ebin/*.app
  80 + @echo "cleaning dependencies:"
  81 + $(REBAR) clean
55 82
56   -ebin/$(APP).app:
57   - cp src/$(APP).app $@
  83 +.PHONY: dist-clean
  84 +dist-clean: clean
  85 + rm -f ./rebar
1  deps/bert.erl
... ... @@ -1 +0,0 @@
1   -Subproject commit 6b142ed266c8f61b543179e01a1500fccc0b7191
1  deps/dh_date
... ... @@ -1 +0,0 @@
1   -Subproject commit 473ad8c47379939aeca919547a7d977eaf34c27b
1  deps/gen_smtp
... ... @@ -1 +0,0 @@
1   -Subproject commit 156b52c16466a0e1dd98395f7c6480b5ff916332
1  deps/lager
... ... @@ -1 +0,0 @@
1   -Subproject commit 18f1806b4cc92516341d89c3af8fd52101fd9dcd
1  deps/mimetypes
... ... @@ -1 +0,0 @@
1   -Subproject commit 0800544bbb684616651a2a1b30aa260132d17d37
1  deps/ua_classifier
... ... @@ -1 +0,0 @@
1   -Subproject commit 5b0835b0bb65b444815ea735b13a4016476aa0f3
1  deps/webzmachine
... ... @@ -1 +0,0 @@
1   -Subproject commit e7e9f46b800f92fadb2eb3247a9b60cce40d4419
1  deps/z_stdlib
... ... @@ -1 +0,0 @@
1   -Subproject commit b46d675aa0697d13acdbf56fcf29a0dbcb8b41bb
10 doc/dev/codestyle.rst
Source Rendered
@@ -109,13 +109,15 @@ Structure your commit message like this::
109 109
110 110 * ``mod_foobar:`` Changes that are related to a single module should
111 111 be prefixed with the module name.
112   - * ``core:`` For changes in the `src`, `include` or `deps` folder;
113   - e.g. everything outside modules.
114 112 * ``doc:`` For changes to the documentation, everything below doc/
115 113 * ``scripts:`` for changes to the ``zotonic`` command and its helper scripts.
  114 + * ``build:`` for the build system and related changes.
116 115 * ``tests:`` for unit tests and the testsandbox.
117   - * ``skel`` for the skeleton sites.
118   - * ``zotonic_status`` for the default site.
  116 + * ``skel:`` for the skeleton sites.
  117 + * ``zotonic_status:`` for the default site.
  118 + * ``translation:`` for new/updated translations.
  119 + * ``core:`` For changes in the `src`, `include` or `deps` folder;
  120 + e.g. anything not covered by another tag.
119 121
120 122 * The **summary** should be less than 50 characters, and tell what was
121 123 changed. Use the imperative present tense (fix, add, change). For
2  doc/ref/controllers/controller_static_pages.rst
Source Rendered
@@ -10,7 +10,7 @@ in a dispatch rule.
10 10
11 11 Example dispatch rule::
12 12
13   - {oldsite, ["old", '*'], resource_static_pages, [{root, "old_site"}]}
  13 + {oldsite, ["old", '*'], controller_static_pages, [{root, "old_site"}]}
14 14
15 15 When a file ``a.txt`` is requested this resource will check for
16 16 ``a.txt`` and ``a.txt.tpl``. When it finds a ``.tpl`` file then that
2  include/zotonic.hrl
@@ -23,7 +23,7 @@
23 23
24 24 -include("zotonic_events.hrl").
25 25
26   --include_lib("deps/webzmachine/include/wm_reqdata.hrl").
  26 +-include_lib("webzmachine/include/wm_reqdata.hrl").
27 27
28 28 %% @doc The request context, session information and other
29 29 -record(context, {
2  modules/mod_base/lib/js/modules/z.dialog.js
@@ -35,7 +35,7 @@
35 35
36 36 var title = $("<div>").addClass("modal-header")
37 37 .append($("<a>").addClass("close").attr("data-dismiss", "modal").html("&times;"))
38   - .append($("<h3>").text(options.title));
  38 + .append($("<h3>").html(options.title));
39 39
40 40 var body = $("<div>").addClass("modal-body")
41 41 .html(options.text);
2  modules/mod_development/controllers/controller_wmtrace.erl
@@ -29,7 +29,7 @@
29 29
30 30
31 31 -include_lib("controller_html_helper.hrl").
32   --include_lib("webmachine_logger.hrl").
  32 +-include_lib("webzmachine/include/webmachine_logger.hrl").
33 33
34 34 is_authorized(ReqData, Context) ->
35 35 z_acl:wm_is_authorized(use, mod_development, ReqData, Context).
2  modules/mod_development/controllers/controller_wmtrace_conf.erl
@@ -29,7 +29,7 @@
29 29
30 30
31 31 -include_lib("controller_html_helper.hrl").
32   --include_lib("webmachine_logger.hrl").
  32 +-include_lib("webzmachine/include/webmachine_logger.hrl").
33 33
34 34 %% GLOBAL
35 35 %% only 5xx
6 modules/mod_survey/templates/blocks/_block_view_survey_truefalse.tpl
@@ -6,10 +6,10 @@
6 6 {% endif %}
7 7 <div class="controls">
8 8 {% if blk.input_type == 'submit' %}
9   - <button class="btn" type="submit" value="1">
  9 + <button id="{{ #yes }}" name="{{ blk.name}}" class="btn" type="submit" value="1">
10 10 <span></span>{{ blk.yes|default:_"True" }}
11 11 </button>
12   - <button class="btn" type="submit" value="0">
  12 + <button id="{{ #no }}" name="{{ blk.name}}" class="btn" type="submit" value="0">
13 13 <span></span>{{ blk.no|default:_"False" }}
14 14 </button>
15 15 {% else %}
@@ -17,7 +17,7 @@
17 17 <input type="radio" id="{{ #yes }}" name="{{ blk.name}}" {% if answers[blk.name] == "true" %}checked="checked"{% endif %} value="1" /> {{ blk.yes|default:_"True" }}
18 18 </label>
19 19 <label class="radio inline">
20   - <input type="radio" id="{{ #no }}" name="{{ blk.name}}" {% if answers[blk.name] == "false" %}checked="checked"{% endif %} value="0" /> {{ blk.yes|default:_"False" }}
  20 + <input type="radio" id="{{ #no }}" name="{{ blk.name}}" {% if answers[blk.name] == "false" %}checked="checked"{% endif %} value="0" /> {{ blk.no|default:_"False" }}
21 21 </label>
22 22 {% if blk.is_required %}
23 23 {% validate id=#yes name=blk.name type={presence} %}
4 modules/mod_survey/templates/blocks/_block_view_survey_yesno.tpl
@@ -6,10 +6,10 @@
6 6 {% endif %}
7 7 <div class="controls">
8 8 {% if blk.input_type == 'submit' %}
9   - <button class="btn" type="submit" value="1">
  9 + <button id="{{ #yes }}" name="{{ blk.name}}" class="btn" type="submit" value="1">
10 10 <span></span>{{ blk.yes|default:_"Yes" }}
11 11 </button>
12   - <button class="btn" type="submit" value="0">
  12 + <button id="{{ #no }}" name="{{ blk.name}}" class="btn" type="submit" value="0">
13 13 <span></span>{{ blk.no|default:_"No" }}
14 14 </button>
15 15 {% else %}
41 rebar.config
... ... @@ -0,0 +1,41 @@
  1 +%% -*- mode: erlang -*-
  2 +{erl_opts,
  3 + [{i, "include"},
  4 + {i, "src/dbdrivers/postgresql/include"},
  5 + {i, "deps/webzmachine/include"},
  6 + {src_dirs,
  7 + ["src",
  8 + "modules",
  9 + "priv/modules",
  10 + "priv/extensions",
  11 + "priv/sites"]
  12 + }
  13 + ]
  14 +}.
  15 +
  16 +{deps,
  17 + [
  18 + {bert, ".*", {git, "git://github.com/zotonic/bert.erl.git", "master"}},
  19 + {dh_date, ".*", {git, "git://github.com/zotonic/dh_date.git", "master"}},
  20 + {gen_smtp, ".*", {git, "git://github.com/zotonic/gen_smtp.git", "master"}},
  21 + {lager, "1.0.0", {git, "git://github.com/basho/lager.git", {tag, "1.0.0"}}},
  22 + {mimetypes, ".*", {git, "git://github.com/zotonic/mimetypes.git", "master"}},
  23 + {ua_classifier, ".*", {git, "git://github.com/zotonic/ua_classifier.git", "master"}},
  24 + {webzmachine, ".*", {git, "git://github.com/zotonic/webzmachine.git", "master"}},
  25 + {z_stdlib, ".*", {git, "git://github.com/zotonic/z_stdlib.git", "master"}}
  26 + ]
  27 +}.
  28 +
  29 +{pre_hooks,
  30 + [
  31 + {compile, "echo `cd deps/iconv && rebar compile`"},
  32 + {compile, "echo `cd deps/mochiweb && rebar compile`"}
  33 + ]
  34 +}.
  35 +
  36 +{post_hooks,
  37 + [
  38 + {clean, "echo `cd deps/iconv && rebar clean`"},
  39 + {clean, "echo `cd deps/mochiweb && rebar clean`"}
  40 + ]
  41 +}.
3  src/erlydtl/erlydtl_compiler.erl
@@ -739,7 +739,8 @@ include_ast(File, Args, All, Context, TreeWalker) ->
739 739 InclusionParseTree,
740 740 Context#dtl_context{
741 741 local_scopes = [ IncludeScope | ContextInclude#dtl_context.local_scopes ],
742   - parse_trail = [FilePath | ContextInclude#dtl_context.parse_trail]},
  742 + parse_trail = [FilePath | ContextInclude#dtl_context.parse_trail],
  743 + extends_trail = [Template]},
743 744 TreeW#treewalker{has_auto_id=false})),
744 745 Ast1 = case InclTW2#treewalker.has_auto_id of
745 746 false -> Ast;
3  src/models/m_media.erl
@@ -316,7 +316,8 @@ replace_file(File, RscId, Props, PropsMedia, Opts, Context) ->
316 316 true ->
317 317 Mime = proplists:get_value(mime, PropsMedia),
318 318 SafeRootName = z_string:to_rootname(proplists:get_value(original_filename, Props, File)),
319   - SafeFilename = SafeRootName ++ z_media_identify:extension(Mime),
  319 + PreferExtension = z_convert:to_binary(filename:extension(proplists:get_value(original_filename, Props, File))),
  320 + SafeFilename = SafeRootName ++ z_media_identify:extension(Mime, PreferExtension),
320 321 ArchiveFile = z_media_archive:archive_copy_opt(File, SafeFilename, Context),
321 322 RootName = filename:rootname(filename:basename(ArchiveFile)),
322 323 MediumRowProps = [
41 src/support/z_media_identify.erl
@@ -30,6 +30,7 @@
30 30 identify_file/3,
31 31 identify_file_direct/2,
32 32 extension/1,
  33 + extension/2,
33 34 guess_mime/1,
34 35 is_mime_compressed/1
35 36 ]).
@@ -233,13 +234,43 @@ mime("PNG32") -> "image/png";
233 234 mime(Type) -> "image/" ++ string:to_lower(Type).
234 235
235 236
236   -%% @doc Return the extension for a known mime type (eg. ".mov")
  237 +
  238 +%% @doc Return the extension for a known mime type (eg. ".mov").
237 239 -spec extension(string()|binary()) -> string().
238 240 extension(Mime) ->
239   - case mimetypes:extensions(z_convert:to_binary(Mime)) of
240   - [Ext|_] -> [$. | z_convert:to_list(Ext)];
241   - [] -> ".bin"
242   - end.
  241 + extension(Mime, undefined).
  242 +
  243 +%% @doc Return the extension for a known mime type (eg. ".mov"). When
  244 +%% multiple extensions are found for the given mime type, returns the
  245 +%% one that is given as the preferred extension. Otherwise, it returns
  246 +%% the first extension.
  247 +-spec extension(string()|binary(), string()|binary()|undefined) -> string().
  248 +extension(Mime, PreferExtension) ->
  249 + Extensions = mimetypes:extensions(z_convert:to_binary(Mime)),
  250 + case PreferExtension of
  251 + undefined ->
  252 + first_extension(Extensions);
  253 + _ ->
  254 + %% convert prefer extension to something that mimetypes likes
  255 + Ext1 = z_convert:to_list(PreferExtension),
  256 + Ext2 = case Ext1 of
  257 + [$.|Rest] ->
  258 + z_convert:to_binary(Rest);
  259 + _ -> z_convert:to_binary(Ext1)
  260 + end,
  261 + case lists:member(Ext2, Extensions) of
  262 + true ->
  263 + [$. | z_convert:to_list(Ext2)];
  264 + false ->
  265 + first_extension(Extensions)
  266 + end
  267 + end.
  268 +
  269 +
  270 +first_extension([]) ->
  271 + ".bin";
  272 +first_extension(Extensions) ->
  273 + [$. | z_convert:to_list(hd(Extensions))].
243 274
244 275
245 276 %% @spec guess_mime(string()) -> string()
37 src/support/z_module_indexer.erl
@@ -111,6 +111,7 @@ find_ua_class_all(What, Class, Name, Context) ->
111 111
112 112 %% @doc Return a list of all templates, scomps etc per module
113 113 all(What, Context) ->
  114 + ActiveDirs = z_module_manager:active_dir(Context),
114 115 [
115 116 #module_index{
116 117 key=#module_index_key{name=F#mfile.name},
@@ -118,7 +119,7 @@ all(What, Context) ->
118 119 filepath=F#mfile.filepath,
119 120 erlang_module=F#mfile.erlang_module
120 121 }
121   - || F <- scan_all(What, Context)
  122 + || F <- scan_all(What, ActiveDirs)
122 123 ].
123 124
124 125
@@ -227,7 +228,8 @@ code_change(_OldVsn, State, _Extra) ->
227 228 %%====================================================================
228 229
229 230 translations1(Context) ->
230   - POs = [{M,F} || #mfile{filepath=F, module=M} <- scan_subdir("translations", "", ".po", Context) ],
  231 + ActiveDirs = z_module_manager:active_dir(Context),
  232 + POs = [{M,F} || #mfile{filepath=F, module=M} <- scan_subdir("translations", "", ".po", ActiveDirs) ],
231 233 ByModule = lists:foldl(fun({M,F}, Acc) ->
232 234 dict:append(M, F, Acc)
233 235 end,
@@ -327,20 +329,21 @@ lookup_class_all(Class, Name, List) ->
327 329
328 330 %% @doc Scan the module directories for scomps, actions etc.
329 331 scan(Context) ->
  332 + ActiveDirs = z_module_manager:active_dir(Context),
330 333 [
331   - {template, scan_subdir_class_files("templates", Context)},
332   - {lib, scan_subdir_class_files("lib", Context)}
  334 + {template, scan_subdir_class_files("templates", ActiveDirs)},
  335 + {lib, scan_subdir_class_files("lib", ActiveDirs)}
333 336 | [
334   - {What, scan_subdir(What, Context)}
  337 + {What, scan_subdir(What, ActiveDirs)}
335 338 || What <- [ scomp, action, validator, model, service ]
336 339 ]
337 340 ].
338 341
339 342
340 343 %% @doc Scan module directories for specific kinds of parts. Returns a lookup list [ {lookup-name, fullpath} ]
341   -scan_subdir(What, Context) ->
  344 +scan_subdir(What, ActiveDirs) ->
342 345 {Subdir, Prefix, Extension} = subdir(What),
343   - scan_subdir(Subdir, Prefix, Extension, Context).
  346 + scan_subdir(Subdir, Prefix, Extension, ActiveDirs).
344 347
345 348 subdir(translation)-> { "translations","", ".po" };
346 349 subdir(scomp) -> { "scomps", "scomp_", ".erl" };
@@ -351,17 +354,17 @@ scan_subdir(What, Context) ->
351 354 subdir(erlang) -> { "support", "", ".erl" }.
352 355
353 356 %% @doc Find all files, for the all/2 function.
354   -scan_all(lib, Context) ->
355   - scan_subdir_class_files("lib", Context);
356   -scan_all(template, Context) ->
357   - scan_subdir_class_files("templates", Context);
358   -scan_all(What, Context) ->
  357 +scan_all(lib, ActiveDirs) ->
  358 + scan_subdir_class_files("lib", ActiveDirs);
  359 +scan_all(template, ActiveDirs) ->
  360 + scan_subdir_class_files("templates", ActiveDirs);
  361 +scan_all(What, ActiveDirs) ->
359 362 {Subdir, Prefix, Extension} = subdir(What),
360   - scan_subdir(Subdir, Prefix, Extension, Context).
  363 + scan_subdir(Subdir, Prefix, Extension, ActiveDirs).
361 364
362 365
363 366 %% @doc Scan the whole subdir hierarchy for files, used for templates and lib folders.
364   -scan_subdir_class_files(Subdir, Context) ->
  367 +scan_subdir_class_files(Subdir, ActiveDirs) ->
365 368 Scan1 = fun({Module, Dir}, Acc) ->
366 369 case z_utils:list_dir_recursive(filename:join(Dir, Subdir)) of
367 370 [] ->
@@ -388,12 +391,12 @@ scan_subdir_class_files(Subdir, Context) ->
388 391 ]
389 392 end
390 393 end,
391   - lists:sort(fun mfile_compare/2, lists:flatten(lists:foldl(Scan1, [], z_module_manager:active_dir(Context)))).
  394 + lists:sort(fun mfile_compare/2, lists:flatten(lists:foldl(Scan1, [], ActiveDirs))).
392 395
393 396
394 397 %% @doc Scan all module directories for templates/scomps/etc. Example: scan("scomps", "scomp_", ".erl", Context)
395 398 %% @spec scan_subdir(Subdir, Prefix, Extension, context()) -> [ {ModuleAtom, {ModuleDir, [{Name, File}]}} ]
396   -scan_subdir(Subdir, Prefix, Extension, Context) ->
  399 +scan_subdir(Subdir, Prefix, Extension, ActiveDirs) ->
397 400 ExtensionRe = case Extension of [] -> []; "."++_ -> "\\" ++ Extension ++ "$" end,
398 401 Scan1 = fun({Module, Dir}, Acc) ->
399 402 {Dir1, Pattern, PrefixLen} =
@@ -421,7 +424,7 @@ scan_subdir(Subdir, Prefix, Extension, Context) ->
421 424 ] | Acc ]
422 425 end
423 426 end,
424   - lists:sort(fun mfile_compare/2, lists:flatten(lists:foldl(Scan1, [], z_module_manager:active_dir(Context)))).
  427 + lists:sort(fun mfile_compare/2, lists:flatten(lists:foldl(Scan1, [], ActiveDirs))).
425 428
426 429
427 430 module2prefix(Module) ->
12 src/support/z_module_manager.erl
@@ -81,12 +81,14 @@ start_link(SiteProps) ->
81 81 %% @spec upgrade(#context{}) -> ok
82 82 %% @doc Reload the list of all modules, add processes if necessary.
83 83 upgrade(Context) ->
  84 + flush(Context),
84 85 gen_server:cast(name(Context), upgrade).
85 86
86 87
87 88 %% @doc Deactivate a module. The module is marked as deactivated and stopped when it was running.
88 89 %% @spec deactivate(Module, #context{}) -> ok
89 90 deactivate(Module, Context) ->
  91 + flush(Context),
90 92 case z_db:q("update module set is_active = false, modified = now() where name = $1", [Module], Context) of
91 93 1 -> upgrade(Context);
92 94 0 -> ok
@@ -97,6 +99,7 @@ deactivate(Module, Context) ->
97 99 %% The module manager can be checked later to see if the module started or not.
98 100 %% @spec activate(Module, #context{}) -> void()
99 101 activate(Module, Context) ->
  102 + flush(Context),
100 103 Scanned = scan(Context),
101 104 {Module, _Dirname} = proplists:lookup(Module, Scanned),
102 105 F = fun(Ctx) ->
@@ -120,8 +123,11 @@ restart(Module, Context) ->
120 123 active(Context) ->
121 124 case z_db:has_connection(Context) of
122 125 true ->
123   - Modules = z_db:q("select name from module where is_active = true order by name", Context),
124   - [ z_convert:to_atom(M) || {M} <- Modules ];
  126 + F = fun() ->
  127 + Modules = z_db:q("select name from module where is_active = true order by name", Context),
  128 + [ z_convert:to_atom(M) || {M} <- Modules ]
  129 + end,
  130 + z_depcache:memo(F, {?MODULE, active, Context#context.host}, Context);
125 131 false ->
126 132 case m_site:get(modules, Context) of
127 133 L when is_list(L) -> L;
@@ -436,6 +442,8 @@ name(Context) ->
436 442 name(Module, #context{host=Host}) ->
437 443 z_utils:name_for_host(Module, Host).
438 444
  445 +flush(Context) ->
  446 + z_depcache:flush({?MODULE, active, Context#context.host}, Context).
439 447
440 448 handle_upgrade(#state{context=Context, sup=ModuleSup} = State) ->
441 449 ValidModules = valid_modules(Context),
2  src/support/z_tracer.erl
@@ -13,7 +13,7 @@
13 13 -export([tracefun/2]). % dbg callback function
14 14
15 15 -include_lib("zotonic.hrl").
16   --include_lib("webmachine_logger.hrl").
  16 +-include_lib("webzmachine/include/webmachine_logger.hrl").
17 17
18 18 -define(TRACED_MOD, z_notifier).
19 19 -define(TRACE_OPTS, []).
30 src/support/z_utils.erl
@@ -695,39 +695,43 @@ replace1(F, T, [C|R], Acc) ->
695 695
696 696 %% @doc Return a list of all files in a directory, recursive depth first search for files not starting with a '.'
697 697 list_dir_recursive(Dir) ->
698   - case file:list_dir(Dir) of
  698 + Sep = separator(element(1, os:type())),
  699 + AbsDir = filename:absname(Dir),
  700 + case file:list_dir(AbsDir) of
699 701 {ok, Files} ->
700   - list_dir_recursive(Files, Dir, []);
  702 + list_dir_recursive(Files, AbsDir, Sep, []);
701 703 {error, _} ->
702 704 []
703 705 end.
704 706
705   - list_dir_recursive([], _BaseDir, Acc) ->
  707 + list_dir_recursive([], _BaseDir, _Sep, Acc) ->
706 708 Acc;
707   - list_dir_recursive([[$.|_]|OtherFiles], BaseDir, Acc) ->
708   - list_dir_recursive(OtherFiles, BaseDir, Acc);
709   - list_dir_recursive([File|OtherFiles], BaseDir, Acc) ->
710   - Path = filename:join(BaseDir, File),
  709 + list_dir_recursive([[$.|_]|OtherFiles], BaseDir, Sep, Acc) ->
  710 + list_dir_recursive(OtherFiles, BaseDir, Sep, Acc);
  711 + list_dir_recursive([File|OtherFiles], BaseDir, Sep, Acc) ->
  712 + Path = [BaseDir, Sep, File],
711 713 case filelib:is_regular(Path) of
712 714 true ->
713   - list_dir_recursive(OtherFiles, BaseDir, [File|Acc]);
  715 + list_dir_recursive(OtherFiles, BaseDir, Sep, [File|Acc]);
714 716 false ->
715 717 case filelib:is_dir(Path) of
716 718 true ->
717 719 Acc1 = case file:list_dir(Path) of
718 720 {ok, Files} ->
719   - NonDotFiles = lists:filter(fun([$.|_]) -> false; (_) -> true end, Files),
720   - RelFiles = [ filename:join(File, F) || F <- NonDotFiles],
721   - list_dir_recursive(RelFiles, BaseDir, Acc);
  721 + RelFiles = [ [File, Sep, F] || [H|_]=F <- Files, H /= $.],
  722 + list_dir_recursive(RelFiles, BaseDir, Sep, Acc);
722 723 {error, _} ->
723 724 Acc
724 725 end,
725   - list_dir_recursive(OtherFiles, BaseDir, Acc1);
  726 + list_dir_recursive(OtherFiles, BaseDir, Sep, Acc1);
726 727 false ->
727   - list_dir_recursive(OtherFiles, BaseDir, Acc)
  728 + list_dir_recursive(OtherFiles, BaseDir, Sep, Acc)
728 729 end
729 730 end.
730 731
  732 + separator(win32) -> $\\;
  733 + separator(_) -> $/.
  734 +
731 735
732 736 %% @doc Check if two arguments are equal, optionally converting them
733 737 are_equal(Arg1, Arg2) when Arg1 =:= Arg2 ->
0  src/zotonic.app → src/zotonic.app.src
File renamed without changes

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.