mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-26 07:19:25 -05:00
Rewrite makefile to better track dependencies.
This commit is contained in:
214
r2/Makefile
214
r2/Makefile
@@ -20,135 +20,161 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
# Javascript files to be compressified
|
||||
js_libs = $(addprefix lib/,json2.js jquery.cookie.js jquery.url.js ui.core.js ui.datepicker.js jquery.flot.js jquery.lazyload.js)
|
||||
js_sources = $(js_libs) jquery.reddit.js reddit.js login.js ui.js base.js sponsored.js compact.js blogbutton.js flair.js analytics.js utils.js
|
||||
js_targets = button.js jquery.flot.js sponsored.js
|
||||
localized_js_targets = reddit.js mobile.js
|
||||
localized_js_outputs = $(localized_js_targets:.js=.*.js)
|
||||
|
||||
# CSS targets
|
||||
main_css = reddit.css
|
||||
main_sprite = $(static_dir)/sprite-main.png
|
||||
compact_css = compact.css
|
||||
compact_sprite = $(static_dir)/sprite-compact.png
|
||||
css_targets = $(main_css) $(compact_css) reddit-ie6-hax.css reddit-ie7-hax.css mobile.css
|
||||
|
||||
package = r2
|
||||
static_dir = $(package)/public/static
|
||||
|
||||
SHELL=/bin/sh
|
||||
PYTHON=python
|
||||
SED=sed
|
||||
CAT=cat
|
||||
PYTHON=python
|
||||
JS_COMPRESS = paster run standalone $(package)/lib/js.py -c "build_reddit_js()"
|
||||
CSS_COMPRESS = $(SED) -e 's/ \+/ /' -e 's/\/\*.*\*\///g' -e 's/: /:/' | grep -v "^ *$$"
|
||||
UPDATE_NAMES = $(PYTHON) $(package)/lib/static.py
|
||||
BUILD_DIR=build
|
||||
|
||||
# If admin codebase is install, get its path so that we can build ini
|
||||
# files against the primary production.ini
|
||||
PRIVATEREPOS = $(shell $(PYTHON) -c 'exec "try: import r2admin; print r2admin.__path__[0]\nexcept:pass"')
|
||||
|
||||
# If reddit-i18n repo is found, use that for r2.pot generation
|
||||
I18NPATH = $(shell $(PYTHON) -c 'exec "try: import reddit_i18n; print reddit_i18n.__path__[0]\nexcept ImportError: print \"r2/i18n\""')
|
||||
|
||||
#------
|
||||
|
||||
JSTARGETS = $(foreach js, $(js_targets) $(localized_js_targets), $(static_dir)/$(js))
|
||||
JSOUTPUTS = $(foreach js, $(js_targets) $(localized_js_outputs), $(static_dir)/$(js))
|
||||
JSSOURCES = $(foreach js, $(js_sources), $(static_dir)/js/$(js))
|
||||
CSSTARGETS = $(foreach css, $(css_targets), $(static_dir)/$(css))
|
||||
SPRITES = $(main_sprite) $(compact_sprite)
|
||||
RTLCSS = $(CSSTARGETS:.css=-rtl.css)
|
||||
NAMES = $(static_dir)/names.json
|
||||
.PHONY: clean
|
||||
all: pyx static ini
|
||||
clean: clean_pyx clean_i18n clean_static
|
||||
|
||||
#################### Cython
|
||||
PYX_FILES := $(shell find . -name \*.pyx)
|
||||
PYXC_FILES := $(PYX_FILES:.pyx=.c)
|
||||
PYXSO_FILES := $(PYX_FILES:.pyx=.so)
|
||||
PYX_C_FILES := $(PYX_FILES:.pyx=.c)
|
||||
PYX_SO_FILES := $(PYX_FILES:.pyx=.so)
|
||||
PYX_BUILDSTAMP := $(BUILD_DIR)/pyx-buildstamp
|
||||
|
||||
NAMED = $(JSOUTPUTS) $(CSSTARGETS) $(RTLCSS) $(SPRITES)
|
||||
NAMELINKS = $(patsubst %.css,%.*.css,$(patsubst %.js,%.*.js,$(NAMED)))
|
||||
GZIPPED = $(foreach file, $(filter %.js %.css, $(wildcard $(NAMELINKS))) $(static_dir)/js/lib/jquery.js, $(file).gz)
|
||||
.PHONY: clean_pyx
|
||||
|
||||
INIUPDATE = $(wildcard *.update)
|
||||
INIS = $(INIUPDATE:.update=.ini)
|
||||
pyx: $(PYX_BUILDSTAMP)
|
||||
|
||||
%.ini: %.update
|
||||
ifneq (,$(PRIVATEREPOS))
|
||||
ln -sf `pwd`/$< $(PRIVATEREPOS)/..
|
||||
make -C $(PRIVATEREPOS)/.. $@
|
||||
ln -sf $(PRIVATEREPOS)/../$@ .
|
||||
else
|
||||
$(PYX_C_FILES): %.c: %.pyx
|
||||
cython $<
|
||||
|
||||
# this won't rebuild pyx if you're a jerk and delete a .so file without deleting the buildstamp
|
||||
# make clean && make pyx will fix this
|
||||
$(PYX_BUILDSTAMP): $(PYX_C_FILES)
|
||||
$(PYTHON) setup.py build_ext --inplace
|
||||
touch $@
|
||||
|
||||
clean_pyx:
|
||||
rm -f $(PYX_BUILDSTAMP) $(PYX_C_FILES) $(PYX_SO_FILES)
|
||||
|
||||
#################### i18n
|
||||
I18NPATH := $(shell $(PYTHON) -c 'from r2.lib.i18n import I18N_PATH; print I18N_PATH')
|
||||
STRINGS_FILE := r2/lib/strings.py
|
||||
RAND_STRINGS_FILE := r2/lib/rand_strings.py
|
||||
POTFILE := $(I18NPATH)/r2.pot
|
||||
|
||||
.PHONY: i18n clean_i18n
|
||||
|
||||
i18n: $(RAND_STRINGS_FILE)
|
||||
$(PYTHON) setup.py extract_messages -o $(POTFILE)
|
||||
|
||||
$(RAND_STRINGS_FILE): $(STRINGS_FILE)
|
||||
paster run standalone $(STRINGS_FILE) -c "print_rand_strings()" > $(RAND_STRINGS_FILE)
|
||||
|
||||
clean_i18n:
|
||||
rm -f $(RAND_STRINGS_FILE)
|
||||
|
||||
#################### ini files
|
||||
INIFILES := $(wildcard *.update)
|
||||
|
||||
ini: $(INIFILES)
|
||||
|
||||
$(INIFILES): %.ini: %.update
|
||||
./updateini.py example.ini $< > $@ || rm $@
|
||||
endif
|
||||
|
||||
#################### Static Files
|
||||
STATIC_DIR := r2/public/static
|
||||
|
||||
all: pyx static names $(INIS)
|
||||
.PHONY: clean_static
|
||||
|
||||
.PHONY: pyx js css rtl static names i18n clean clean_static clean_pyx all gzip clean_gzip
|
||||
static: pyx css js names
|
||||
|
||||
$(NAMES): $(JSTARGETS) $(CSSTARGETS) $(RTLCSS) $(SPRITES)
|
||||
$(UPDATE_NAMES) $(NAMES) $(NAMED)
|
||||
clean_static: clean_css clean_js clean_names
|
||||
|
||||
$(JSTARGETS): $(JSSOURCES)
|
||||
$(JS_COMPRESS)
|
||||
#### Stylesheets
|
||||
CSS_COMPRESS := $(SED) -e 's/ \+/ /' -e 's/\/\*.*\*\///g' -e 's/: /:/' | grep -v "^ *$$"
|
||||
CSS_SOURCE_DIR := $(STATIC_DIR)/css
|
||||
|
||||
$(main_sprite) $(static_dir)/$(main_css): $(static_dir)/css/$(main_css)
|
||||
rm -f $@ # delete symlink so we don't just overwrite the old mangled file
|
||||
$(PYTHON) r2/lib/nymph.py $< $(static_dir)/sprite-main.png | $(CSS_COMPRESS) > $@
|
||||
SPRITED_STYLESHEETS := reddit.css compact.css
|
||||
PROCESSED_SPRITED_STYLESHEETS := $(addprefix $(STATIC_DIR)/, $(SPRITED_STYLESHEETS))
|
||||
SPRITES := $(addprefix $(STATIC_DIR)/, $(patsubst %.css,sprite-%.png, $(SPRITED_STYLESHEETS)))
|
||||
|
||||
$(compact_sprite) $(static_dir)/$(compact_css) : $(static_dir)/css/$(compact_css)
|
||||
rm -f $@ # delete symlink so we don't just overwrite the old mangled file
|
||||
$(PYTHON) r2/lib/nymph.py $< $(static_dir)/sprite-compact.png | $(CSS_COMPRESS) > $@
|
||||
OTHER_STYLESHEETS := reddit-ie6-hax.css reddit-ie7-hax.css mobile.css
|
||||
MINIFIED_OTHER_STYLESHEETS := $(addprefix $(STATIC_DIR)/, $(OTHER_STYLESHEETS))
|
||||
|
||||
$(static_dir)/%.css : $(static_dir)/css/%.css
|
||||
PROCESSED_STYLESHEETS := $(PROCESSED_SPRITED_STYLESHEETS) $(MINIFIED_OTHER_STYLESHEETS)
|
||||
RTL_STYLESHEETS := $(PROCESSED_STYLESHEETS:.css=-rtl.css)
|
||||
|
||||
CSS_OUTPUTS = $(PROCESSED_STYLESHEETS) $(RTL_STYLESHEETS) $(SPRITES)
|
||||
|
||||
.PHONY: clean_css
|
||||
|
||||
css: $(CSS_OUTPUTS)
|
||||
|
||||
$(MINIFIED_OTHER_STYLESHEETS): $(STATIC_DIR)/%.css: $(CSS_SOURCE_DIR)/%.css
|
||||
$(CAT) $< | $(CSS_COMPRESS) > $@
|
||||
|
||||
$(RTLCSS): %-rtl.css : %.css
|
||||
$(STATIC_DIR)/sprite-%.png $(STATIC_DIR)/%.css: $(CSS_SOURCE_DIR)/%.css
|
||||
# when static file names are mangled, the original becomes a symlink to the mangled name
|
||||
# remove the original file here in case it's a symlink so we don't just rewrite the old file
|
||||
rm -f $(STATIC_DIR)/$*.css
|
||||
$(PYTHON) r2/lib/nymph.py $< $(STATIC_DIR)/sprite-$*.png | $(CSS_COMPRESS) > $(STATIC_DIR)/$*.css
|
||||
|
||||
$(RTL_STYLESHEETS): %-rtl.css : %.css
|
||||
$(SED) -e "s/left/>####</g" \
|
||||
-e "s/right/left/g" \
|
||||
-e "s/>####</right/g" \
|
||||
-e "s/\(margin\|padding\):\s*\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)/\1:\2 \5 \4 \3/g" $< > $@
|
||||
|
||||
%.c: %.pyx
|
||||
cython $<
|
||||
clean_css:
|
||||
rm -f $(CSS_OUTPUTS)
|
||||
|
||||
$(PYXSO_FILES): $(PYXC_FILES)
|
||||
python setup.py build_ext --inplace
|
||||
#### JS
|
||||
JS_MODULES := $(shell $(PYTHON) r2/lib/js.py enumerate_modules)
|
||||
JS_MODULE_BUILDSTAMPS := $(foreach module,$(JS_MODULES),$(BUILD_DIR)/$(module)-js-buildstamp)
|
||||
JS_OUTPUTS := $(shell $(PYTHON) r2/lib/js.py enumerate_outputs)
|
||||
|
||||
pyx: $(PYXSO_FILES)
|
||||
.PHONY: clean_js
|
||||
|
||||
static: js css rtl
|
||||
js: $(JS_MODULE_BUILDSTAMPS)
|
||||
|
||||
js: pyx $(JSTARGETS)
|
||||
$(JS_OUTPUTS): $(JS_MODULE_BUILDSTAMPS)
|
||||
|
||||
css: $(CSSTARGETS) $(SPRITES)
|
||||
define JS_MODULE_TEMPLATE
|
||||
$(BUILD_DIR)/$(1)-js-buildstamp: $$(shell $(PYTHON) r2/lib/js.py dependencies $(1))
|
||||
paster run standalone r2/lib/js.py -c "build_module('$(1)')"
|
||||
touch $$@
|
||||
endef
|
||||
|
||||
rtl: $(RTLCSS)
|
||||
# apply the module template to each of the modules
|
||||
# so they source their deps from js.py and build accordingly
|
||||
$(foreach module,$(JS_MODULES),$(eval $(call JS_MODULE_TEMPLATE,$(module))))
|
||||
|
||||
names: $(NAMES)
|
||||
clean_js:
|
||||
rm -f $(JS_MODULE_BUILDSTAMPS) $(STATIC_DIR)/*.js
|
||||
|
||||
# Not part of 'all' target as it doesn't need to be run for the site to work,
|
||||
# only when r2.pot needs updating
|
||||
i18n:
|
||||
paster run run.ini r2/lib/strings.py -c "print_rand_strings()" > r2/lib/rand_strings.py
|
||||
python setup.py extract_messages -o $(I18NPATH)/r2.pot
|
||||
#### name mangling
|
||||
MANGLEABLE_FILES := $(CSS_OUTPUTS) $(JS_OUTPUTS)
|
||||
MANGLE_BUILDSTAMP := $(BUILD_DIR)/mangle-buildstamp
|
||||
NAMES_FILE := $(STATIC_DIR)/names.json
|
||||
MANGLED_FILES := $(wildcard $(foreach file,$(MANGLEABLE_FILES),$(basename $(file)).*$(suffix $(file))))
|
||||
|
||||
.PHONY: clean_names
|
||||
|
||||
names: $(MANGLE_BUILDSTAMP)
|
||||
|
||||
$(MANGLE_BUILDSTAMP): $(MANGLEABLE_FILES)
|
||||
$(PYTHON) r2/lib/static.py $(NAMES_FILE) $(MANGLEABLE_FILES)
|
||||
touch $@
|
||||
|
||||
clean_names:
|
||||
rm -f $(MANGLE_BUILDSTAMP) $(NAMES_FILES) $(MANGLED_FILES)
|
||||
|
||||
#### gzip!
|
||||
GZIPPABLE := $(filter %.css %.js,$(MANGLED_FILES) $(STATIC_DIR)/js/lib/jquery.js)
|
||||
GZIPPED := $(addsuffix .gz,$(GZIPPABLE))
|
||||
|
||||
.PHONY: clean_gzip
|
||||
|
||||
gzip: $(GZIPPED)
|
||||
|
||||
%.gz: %
|
||||
$(GZIPPED): %.gz: %
|
||||
gzip -c $< > $@
|
||||
|
||||
clean: clean_static clean_pyx clean_gzip
|
||||
rm -f $(INIS) r2/lib/rand_strings.py
|
||||
|
||||
clean_pyx:
|
||||
rm -f $(PYXSO_FILES) $(PYXC_FILES)
|
||||
|
||||
clean_static: clean_names
|
||||
rm -f $(JSTARGETS) $(JSOUTPUTS) $(CSSTARGETS) $(RTLCSS) $(SPRITES)
|
||||
|
||||
clean_names:
|
||||
rm -f $(NAMES) $(NAMELINKS) $(static_dir)/*.md5
|
||||
|
||||
clean_gzip:
|
||||
rm -f $(static_dir)/*.css.gz $(static_dir)/*.js.gz $(static_dir)/js/lib/*.gz
|
||||
rm -f $(GZIPPED)
|
||||
|
||||
108
r2/r2/lib/js.py
108
r2/r2/lib/js.py
@@ -4,7 +4,15 @@ import os.path
|
||||
from subprocess import Popen, PIPE
|
||||
import re
|
||||
import json
|
||||
from pylons import g, c
|
||||
|
||||
from r2.lib.i18n import get_available_languages
|
||||
|
||||
if __name__ != "__main__":
|
||||
from pylons import g, c
|
||||
STATIC_ROOT = g.paths["static_files"]
|
||||
else:
|
||||
REDDIT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
STATIC_ROOT = os.path.join(REDDIT_ROOT, "public")
|
||||
|
||||
script_tag = '<script type="text/javascript" src="{src}"></script>\n'
|
||||
inline_script_tag = '<script type="text/javascript">{content}</script>\n'
|
||||
@@ -35,11 +43,19 @@ class Source(object):
|
||||
"""An abstract collection of JavaScript code."""
|
||||
def get_source(self):
|
||||
"""Return the full JavaScript source code."""
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
|
||||
def use(self):
|
||||
"""Return HTML to insert the JavaScript source inside a template."""
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def dependencies(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
raise NotImplementedError
|
||||
|
||||
class FileSource(Source):
|
||||
"""A JavaScript source file on disk."""
|
||||
@@ -52,7 +68,7 @@ class FileSource(Source):
|
||||
@property
|
||||
def path(self):
|
||||
"""The path to the source file on the filesystem."""
|
||||
return os.path.join(g.paths["static_files"], "static/js", self.name)
|
||||
return os.path.join(STATIC_ROOT, "static", "js", self.name)
|
||||
|
||||
def use(self):
|
||||
from r2.lib.template_helpers import static
|
||||
@@ -61,6 +77,10 @@ class FileSource(Source):
|
||||
path.insert(1, "js")
|
||||
return script_tag.format(src=static(os.path.join(*path)))
|
||||
|
||||
@property
|
||||
def dependencies(self):
|
||||
return [self.path]
|
||||
|
||||
class Module(Source):
|
||||
"""A module of JS code consisting of a collection of sources."""
|
||||
def __init__(self, name, *sources):
|
||||
@@ -78,7 +98,7 @@ class Module(Source):
|
||||
@property
|
||||
def path(self):
|
||||
"""The destination path of the module file on the filesystem."""
|
||||
return os.path.join(g.paths["static_files"], "static", self.name)
|
||||
return os.path.join(STATIC_ROOT, "static", self.name)
|
||||
|
||||
def build(self, closure):
|
||||
print >> sys.stderr, "Compiling {0}...".format(self.name),
|
||||
@@ -93,6 +113,17 @@ class Module(Source):
|
||||
else:
|
||||
return script_tag.format(src=static(self.name))
|
||||
|
||||
@property
|
||||
def dependencies(self):
|
||||
deps = []
|
||||
for source in self.sources:
|
||||
deps.extend(source.dependencies)
|
||||
return deps
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
return [self.path]
|
||||
|
||||
class StringsSource(Source):
|
||||
"""A virtual source consisting of localized strings from r2.lib.strings."""
|
||||
def __init__(self, lang=None, keys=None, prepend="r.strings = "):
|
||||
@@ -132,6 +163,12 @@ class LocalizedModule(Module):
|
||||
A StringsSource is created and included which contains localized versions
|
||||
of the strings referenced in the module.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def languagize_path(path, lang):
|
||||
path_name, path_ext = os.path.splitext(path)
|
||||
return path_name + "." + lang + path_ext
|
||||
|
||||
def build(self, closure):
|
||||
Module.build(self, closure)
|
||||
|
||||
@@ -139,11 +176,16 @@ class LocalizedModule(Module):
|
||||
string_keys = re.findall("r\.strings\.([\w$_]+)", reddit_source)
|
||||
|
||||
print >> sys.stderr, "Creating language-specific files:"
|
||||
path_name, path_ext = os.path.splitext(self.path)
|
||||
for lang in g.languages:
|
||||
for lang in get_available_languages():
|
||||
strings = StringsSource(lang, string_keys)
|
||||
source = strings.get_source()
|
||||
lang_path = path_name + "." + lang + path_ext
|
||||
lang_path = LocalizedModule.languagize_path(self.path, lang)
|
||||
|
||||
# make sure we're not rewriting a different mangled file
|
||||
# via symlink
|
||||
if os.path.islink(lang_path):
|
||||
os.unlink(lang_path)
|
||||
|
||||
with open(lang_path, "w") as out:
|
||||
print >> sys.stderr, " " + lang_path
|
||||
out.write(reddit_source+source)
|
||||
@@ -155,10 +197,15 @@ class LocalizedModule(Module):
|
||||
if g.uncompressedJS:
|
||||
return embed + StringsSource().use()
|
||||
else:
|
||||
name, ext = os.path.splitext(self.name)
|
||||
url = name + "." + get_lang()[0] + ext
|
||||
url = LocalizedModule.languagize_path(self.name, get_lang()[0])
|
||||
return script_tag.format(src=static(url))
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
languages = get_available_languages()
|
||||
for lang in languages:
|
||||
yield LocalizedModule.languagize_path(self.path, lang)
|
||||
|
||||
class JQuery(Module):
|
||||
def __init__(self, cdn_src=None):
|
||||
Module.__init__(self, os.path.join("js", "lib", "jquery.js"))
|
||||
@@ -175,6 +222,14 @@ class JQuery(Module):
|
||||
ext = ".js" if g.uncompressedJS else ".min.js"
|
||||
return script_tag.format(src=self.cdn_src+ext)
|
||||
|
||||
@property
|
||||
def dependencies(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def outputs(self):
|
||||
return []
|
||||
|
||||
module = {}
|
||||
|
||||
module["jquery"] = JQuery()
|
||||
@@ -218,10 +273,31 @@ module["flot"] = Module("jquery.flot.js",
|
||||
def use(*names):
|
||||
return "\n".join(module[name].use() for name in names)
|
||||
|
||||
def build_reddit_js():
|
||||
closure = ClosureCompiler("r2/lib/contrib/closure_compiler/compiler.jar")
|
||||
for name in module:
|
||||
module[name].build(closure)
|
||||
commands = {}
|
||||
def build_command(fn):
|
||||
commands[fn.__name__] = fn
|
||||
return fn
|
||||
|
||||
if __name__=="__main__":
|
||||
build_reddit_js()
|
||||
@build_command
|
||||
def enumerate_modules():
|
||||
for m in module:
|
||||
print m
|
||||
|
||||
@build_command
|
||||
def dependencies(name):
|
||||
for dep in module[name].dependencies:
|
||||
print dep
|
||||
|
||||
@build_command
|
||||
def enumerate_outputs():
|
||||
for m in module.itervalues():
|
||||
for output in m.outputs:
|
||||
print output
|
||||
|
||||
@build_command
|
||||
def build_module(name):
|
||||
closure = ClosureCompiler("r2/lib/contrib/closure_compiler/compiler.jar")
|
||||
module[name].build(closure)
|
||||
|
||||
if __name__ == "__main__":
|
||||
commands[sys.argv[1]](*sys.argv[2:])
|
||||
|
||||
Reference in New Issue
Block a user