Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2892405
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
33 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/docs/userguide/arcanist_commit_ranges.diviner b/src/docs/userguide/arcanist_commit_ranges.diviner
index 59e31742c1..71977bb229 100644
--- a/src/docs/userguide/arcanist_commit_ranges.diviner
+++ b/src/docs/userguide/arcanist_commit_ranges.diviner
@@ -1,305 +1,305 @@
@title Arcanist User Guide: Commit Ranges
@group userguide
Explains how commit ranges work in Arcanist.
= Overview =
//In Subversion, `arc` commands always operate on the uncommitted changes in the
working copy. If you use Subversion, this document is not relevant to you.//
In Git and Mercurial, many `arc` commands (notably, `arc diff`) operate on a
range of commits beginning with some commit you specify and ending with the
working copy state.
Since the end of the range is fixed (the working copy state), you only need to
specify the beginning of the range. This is called the "base commit". You can do
this explicitly when you run commands:
$ arc diff HEAD^ # git: just the most recent commit
$ arc diff .^ # hg: just the most recent commit
You can also configure `arc` so it defaults to some base commit, or figures out
the base commit using a (potentially sophisticated) ruleset.
NOTE: This document describes a new mechanism for determining base commits. It
is subject to change. There are currently several other mechanisms available as
well, mentioned in other documents. As this mechanism matures, it should replace
other mechanisms and defaults.
= Configuring Base Commit Rules =
Base commit rule configuration may be more complicated than you expect. This is
because people use many different workflows in Git and Mercurial, and have very
different expectations about what base commit `arc` should pick when run. To
make matters worse, some of the most common ways of thinking about which commits
represent a change are incompatible when selecting defaults.
Historically, we tried to use a number of heuristics and simpler approaches to
determine the base commit, but there is so much diversity in how people think
about version control and what they expect to happen that some users were always
unhappy.
Although ruleset configuration is fairly complex, it's powerful enough that you
should be able to get exactly the behavior you want.
To determine the base commit, `arc` processes //rules// one at a time until it
gets a match (a rule which identifies a valid commit). The first match is the
base commit that is used to determine the beginning of the commit range.
A //rule// looks like this:
arc:upstream
A rule may //match//, meaning that it identifies some valid commit in the
working copy, or //fail//, meaning that it does not identify a valid commit. For
instance, the rule `arc:upstream` will //match// if the current Git branch
tracks an upstream branch, but //fail// if the current Git branch does not track
-an upstream branch. When a rule fails, processing continues with the next rule.
-Some rules can never match but produce useful side effects instead. These are
-described below.
+an upstream branch, or the working copy isn't a Git working copy. When a rule
+fails, processing continues with the next rule. Some rules can never match but
+produce useful side effects instead. These are described below.
A //ruleset// is a comma-separated list of rules:
arc:upstream, arc:prompt
-`arc` reads four rulesets:
+`arc` reads five rulesets:
# `args`, specified with `--base <ruleset>` on the command line when you run
a command. This ruleset is processed first.
# `local`, specified with `arc set-config --local base <ruleset>`. This
ruleset is local to the working copy where it is set, and is processed
second.
# `project`, specified by setting the "base" key in `.arcconfig`. This
ruleset is bound to the project where it is configured, and is processed
third.
# `global`, specified with `arc set-config base <ruleset>`. This ruleset is
global for the current user, and is processed fourth.
# `system`, specified in a system-wide configuration file. This ruleset is
global for all users on the system, and is processed last.
The rules in each ruleset are processed one at a time until a valid base commit
is found. Valid rules are listed below. In this list, "*" means "any string".
- `git:*` Use the specified symbolic commit, if it exists.
- `git:merge-base(*)` Use the merge-base of HEAD and the specified symbolic
commit, if it exists.
- `git:branch-unique(*)` Attempt to select changes unique to this branch (that
is, changes between the branch point and HEAD). This rule is complicated and
has limitations, see below for a detailed description.
- `hg:*` Use the specified symbolic commit, if it exists.
- `hg:gca(*)` Use the greatest common ancestor of `.` and the specified
symbolic commit, if it exists.
- `arc:upstream` Use the merge-base of the current branch's upstream and
HEAD, if it exists. (git-only)
- `arc:outgoing` Use the most recent non-outgoing ancestor of the working
copy parent. (hg-only)
- `arc:exec(*)` Execute the specified command. The command should determine
the base revision to use and print it to stdout, then exit with return code
0. If the command exits with another return code, the rule will fail. The
command will be executed with the root directory of the working copy as the
current working directory.
- `arc:bookmark` Use the most recent non-outgoing ancestor of the working
copy parent or the most recent bookmark, whichever is more recent. This
rule is complicated and has limitations, see below for a detailed
description.
- `arc:amended` Use the current commit (`HEAD` in Git, or `.` in Mercurial) if
it has been amended to include a "Differential Revision:" field. Otherwise,
fail.
- `arc:prompt` Prompt the user to provide a commit.
- `arc:empty` Use the empty state (as though the repository were completely
empty, the range will include every commit that is an ancestor of the
working copy).
Rules are also available which change the processing order of rulesets:
- `arc:args`, `arc:local`, `arc:project`, `arc:global`, `arc:system` Stop
processing the current ruleset and begin processing the specified ruleset.
The current ruleset will resume processing after the specified ruleset is
exhausted.
- `arc:yield` Stop processing the current ruleset and begin processing the
next ruleset. The current ruleset will resume processing after other
rulesets have processed or when it next appears in the processing order,
whichever comes first.
- `arc:halt` Stops processing all rules. This will cause the command you ran
to fail, but can be used to avoid running rules which would otherwise
be processed later.
Additionally, there are some rules which are probably useful mostly for testing
or debugging rulesets:
- `arc:verbose` Turns on verbose logging of rule processing.
- `arc:skip` This rule has no effect.
- `literal:*` Use the specified commit literally. Almost certainly wrong in
production rules.
= Examples =
Diff against `origin/master` if it exists, and prompt if it doesn't:
git:merge-base(origin/master), arc:prompt
Diff against the upstream if it exists, or just use the last commit if it
doesn't:
arc:upstream, git:HEAD^
As a user, ignore project rules and always use my rules:
(local) arc:global, arc:halt
As a project maintainer, respect user rules over project rules:
(project) arc:yield, <defaults>
Debug your rules:
$ arc diff --base arc:verbose
Understand rules processing:
$ arc which
$ arc which --base '<ruleset>'
$ arc which --base 'arc:verbose, <ruleset>'
= Detailed Rule Descriptions =
Some rules have complex operation, described here in more detail. These rules
are advanced features for expert users wishing to optimize their workflow and
save a little typing. You do not need to understand the behavior of these rules
to use `arc` (you can always specify a base commit explicitly).
== git:branch-unique(*) ==
This rule only works in Git.
This rule tries to find commits that are unique to the current branch. It is
most likely to be useful if you develop using one branch per feature, update
changes by amending commits (instead of stacking commits) and merge changes by
rebasing (instead of merging).
The rule operates by first determining the merge-base of the specified commit
and HEAD, if it exists. If no such commit exists, the rule fails. If such a
commit exists, the rule counts how many branches contain HEAD, then walks from
HEAD to the merge-base commit, counting how many branches contain each commit.
It stops when it finds a commit which appears on more branches than HEAD,
or when it reaches the merge-base commit.
This rule works well for trees that look like this:
| * Commit B1, on branch "subfeature" (HEAD)
| /
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
This tree represents using feature branches to develop one feature ("feature"),
and then creating a sub-branch to develop a dependent feature ("subfeature").
Normally, if you run `arc diff` on branch "subfeature" (with HEAD at `B1`), a
rule like `arc:merge-base(master)` will select `M1` as the base commit and thus
incorrectly include `A1` in the commit range.
For trees like this, `git:branch-unique(master)` will instead select `A1` as the
base commit (because it is the first commit between `B1` and `M1` which appears
on more branches than `B1` -- `B1` appears on only "subfeature" while `A1`
appears on "subfeature" and "feature") and include only `B1` in the commit
range.
The rule will also do the right thing when run from "feature" in this case.
However, this rule will select the wrong commit range in some cases. For
instance, it will do the wrong thing in this tree:
|
| * Commit A2, on branch "feature" (HEAD)
| |
| | * Commit B1, on branch "subfeature"
| |/
| * Commit A1, on branch "feature"
|/
* Commit M1, on branch "master"
|
This tree represents making another commit (`A2`) on "feature", on top of `A1`.
Here, when `arc diff` is run from branch "feature" (with HEAD at `A2`), this
rule will incorrectly select only `A2` because `A2` (which is HEAD) appears on
one branch ("feature") while `A1` appears on two branches ("feature",
"subfeature").
You can avoid this problem by amending changes into `A1` instead of adding new
commits, or by rebasing "subfeature" before running `arc diff`.
This rule will also select the wrong commit range in a tree like this:
|
| * Commit A1', on branch "feature", created by amending A1
| |
| | * Commit B1, on branch "subfeature" (HEAD)
| |/
| o Commit A1, no longer on "feature" but still on "subfeature"
|/
* Commit M1, on branch "master"
|
This tree represents amending `A1` without rebasing "subfeature", so that `A1`
is no longer on "feature" (replaced with `A1'`) but still on "subfeature". In
this case, running `arc diff` from "subfeature" will incorrectly select both
`B1` and `A1`, because they now are contained by the same number of branches.
You can avoid this problem by rebasing sub-branches before running `arc diff`,
or by using a rule like `arc:amended` before `git:branch-unique(*)`.
== arc:bookmark ==
This rule only works in Mercurial.
This rule finds outgoing changes, but stops when it encounters a bookmark. It is
most likely to be useful if you use one bookmark per feature.
This rule operates like `arc:outgoing`, but then walks the commits between
`.` and the selected base commit. It stops when it encounters a bookmark. For
example, if you have a tree like this:
|
| * C4 (outgoing, bookmark: stripes)
| |
| * C3 (outgoing, bookmark: zebra)
| |
| * C2 (outgoing, no bookmark)
|/
* C1 (pushed, no bookmark)
|
When run from `C4`, this rule will select just `C4`, stopping on `C3` because
it has a different bookmark. When run from `C3`, it will select `C2` and `C3`.
However, this rule will select the wrong commit range in some cases (for
example, if the "zebra" bookmark has moved on, the rule will no longer stop on
-`C3` and will select `C2`, `C3` and `C4` when run from `C4).
+`C3` and will select `C2`, `C3` and `C4` when run from `C4`).
== arc:exec(*) ==
This rule runs some external script or shell command. It is intended for
advanced users who want specialized behavior that can't be expressed with other
rules.
To use this rule, provide some script or shell command. For example:
arc:exec(git merge-base origin/master HEAD)
arc:exec(/path/to/some/script.sh)
The command will be executed with the working copy as its working directory,
and passed no arguments. To //match//, it should print the name of a base commit
on stdout and then exit with return code 0. To //fail//, it should exit with
any other return code.
= Next Steps =
Continue by:
- learning about `arc diff` in more detail with
@{article:Arcanist User Guide: arc diff}; or
- returning to @{article:Arcanist User Guide}.
diff --git a/src/docs/userguide/arcanist_lint_unit.diviner b/src/docs/userguide/arcanist_lint_unit.diviner
index 25ca2a273c..0b24008476 100644
--- a/src/docs/userguide/arcanist_lint_unit.diviner
+++ b/src/docs/userguide/arcanist_lint_unit.diviner
@@ -1,89 +1,89 @@
@title Arcanist User Guide: Customizing Lint, Unit Tests and Workflows
@group userguide
Explains how to build new classes to control how Arcanist behaves.
= Overview =
Arcanist has some basic configuration options available in the `.arcconfig`
file (see @{article:Arcanist User Guide: Configuring a New Project}), but it
can't handle everything. If you want to customize Arcanist at a deeper level,
you need to build new classes. For instance:
- if you want to configure linters, or add new linters, you need to create a
new class which extends @{class@arcanist:ArcanistLintEngine}.
- if you want to integrate with a unit testing framework, you need to create a
new class which extends @{class@arcanist:ArcanistBaseUnitTestEngine}.
- if you you want to change how workflows behave, or add new workflows, you
need to create a new class which extends
@{class@arcanist:ArcanistConfiguration}.
Arcanist works through a sort of dependency-injection approach. For example,
Arcanist does not run lint rules by default, but you can set `lint.engine`
in your `.arcconfig` to the name of a class which extends
@{class@arcanist:ArcanistLintEngine}. When running from inside your project,
Arcanist will load this class and call methods on it in order to run lint. To
make this work, you need to do three things:
- actually write the class;
- add the library where the class exists to your ##.arcconfig##;
- add the class name to your ##.arcconfig## as the **lint.engine**,
**unit.engine**, or **arcanist_configuration**.
= Create a libphutil Library =
If you haven't created a library for the class to live in yet, you need to do
that first. Follow the instructions in @{article:libphutil Libraries User Guide},
then make the library loadable by adding it to your ##.arcconfig## like this:
{
// ...
- "phutil_libraries" : [
+ "load" : [
// ...
"/path/to/my/library", // Absolute path
"support/arcanist", // Relative path in this project
// ...
]
// ...
}
You can either specify an absolute path, or a path relative to the project root.
When you run ##arc list --trace##, you should see a message to the effect that
it has loaded your library.
For debugging or testing, you can also run Arcanist with the
##--load-phutil-library## flag:
arc --load-phutil-library=/path/to/library <command>
You can specify this flag more than once to load several libraries. Note that
if you use this flag, Arcanist will ignore any libraries listed in
##.arcconfig##.
= Use the Class =
This step is easy: just edit ##.arcconfig## to specify your class name as
the appropriate configuration value.
{
// ...
"lint.engine" : "CustomArcanistLintEngine",
// ...
}
Now, when you run Arcanist in your project, it will invoke your class when
appropriate.
For lint and unit tests, you can also use the ##--engine## flag override the
default engine:
arc lint --engine MyCustomArcanistLintEngine
This is mostly useful for debugging and testing.
= Next Steps =
- Browse the source for an example lint engine at
@{class@arcanist:ExampleLintEngine}; or
- learn how to reuse existing linters by reading
@{article:Arcanist User Guide: Customizing Existing Linters}.
diff --git a/src/docs/userguide/arcanist_new_project.diviner b/src/docs/userguide/arcanist_new_project.diviner
index 0e09be87df..941c5c72f2 100644
--- a/src/docs/userguide/arcanist_new_project.diviner
+++ b/src/docs/userguide/arcanist_new_project.diviner
@@ -1,176 +1,195 @@
@title Arcanist User Guide: Configuring a New Project
@group userguide
Explains how to configure Arcanist projects with ##.arcconfig## files.
= Overview =
You can run `arc` commands that require a working copy in any Git, Subversion
or Mercurial working copy, but some features won't work unless you set up an
`.arcconfig` file to configure settings for the project. Creating this file is
easy and only takes a few minutes.
Without `.arcconfig`:
- You will need to set a default Phabricator URI with
`arc set-config default <uri>`, or specify an explicit URI
with `--conduit-uri` each time you run a command.
- You will not be able to run linters through arc unless you pass `--engine`
explicitly.
- You will not be able to customize certain linter parameters even with
`--engine`.
- You will not be able to run unit tests through arc unless you pass
`--engine` explicitly.
- You will not be able to trigger lint and unit integration through
`arc diff`.
- You will not be able to put Git working copies into immutable history mode
(see below).
- You will not be able to specify a repository encoding. UTF-8 will be assumed
if you do not pass `--encoding`.
- You will not be able to add plugins to arc to modify existing workflows or
add new ones.
- You will not be able to load additional libraries unless you specify them
explicitly with `--load-phutil-library`.
- Symbol index integration, which allows users to click function or class
names in Differential and jump to their definitions, will not work.
- `arc patch` will be unable to detect that you are applying changes to the
wrong project.
- In Subversion, `arc` will be unable to determine the canonical root
of a project, and will assume it is the working directory (in Subversion
prior to 1.7) or the root of the checkout (in Subversion after 1.7). This
means the paths of files in diffs won't be anchored to the same place,
and will have different amounts of path context, which may be confusing for
reviewers and will sometimes prevent patches from applying properly if they
are applied against a different directory than they were generated from.
- In Subversion, `arc` will be unable to guess that you intend to update
an existing revision; you must use `--update` explicitly or `--preview`
and attach diffs via the web interface.
= .arcconfig Basics =
Arcanist uses ##.arcconfig## files to determine a number of things about project
configuration. For instance, these are things it figures out from
##.arcconfig##:
- where the logical root directory of a project is;
- which server Arcanist should send diffs to for code review; and
- which lint rules should be applied.
An ##.arcconfig## file is a JSON file which you check into your project's root.
A simple, valid file looks something like this:
{
"project_id" : "some_project_name",
"conduit_uri" : "https://phabricator.example.com/"
}
Here's what these options mean:
- **project_id**: a human-readable string identifying the project
- **conduit_uri**: the URI for the Phabricator installation that Arcanist
should send diffs to for review. Be mindful about "http" vs "https".
For an exhaustive list of available options, see below.
= Advanced .arcconfig =
Other options include:
- **lint.engine**: the name of a subclass of
@{class@arcanist:ArcanistLintEngine}, which should be used to apply lint
rules to this project. See @{article:Arcanist User Guide: Lint}.
- **unit.engine**: the name of a subclass of
@{class@arcanist:ArcanistBaseUnitTestEngine}, which should be used to apply
unit test rules to this project. See
@{article:Arcanist User Guide: Customizing Lint, Unit Tests and Workflows}.
- **arcanist_configuration**: the name of a subclass of
@{class@arcanist:ArcanistConfiguration} which can add new command flags for
this project or provide entirely new commands.
- - **copyright_holder**: used by @{class@arcanist:ArcanistLicenseLinter} to
- apply license notices to source files.
- - **immutable_history**: controls how `arc diff` behaves in Git. See below.
- - **phutil_libraries**: map of additional Phutil libraries to load at startup.
+ - **history.immutable**: controls how `arc diff` and some other commands
+ behave in Git and Mercurial. See below.
+ - **load**: list of additional Phutil libraries to load at startup.
See below for details about path resolution, or see
@{article:libphutil Libraries User Guide} for a general introduction to
libphutil libraries.
-= Immutable History =
+= History Mutability =
-There are a lot of ways to use git, and Arcanist is flexible enough to handle
-several of them. Git workflows divide into two major groups based on your
-**doctrine of history mutability**.
+Arcanist workflows run in two broad modes: either history is //mutable// or
+//immutable//. Under a //mutable// history, `arc` commands may rewrite the
+working copy history; under an //immutable// history, they may not.
-Choose a **history mutability doctrine** by setting ##"immutable_history"## in
-your ##.arcconfig##. Valid values are ##true## to enforce a **conservative
-history mutability doctrine** or ##false## to enforce a **liberal history
-mutability doctrine**. The default is ##false##.
+You control history mutability by setting `history.immutable` to `true` or
+`false` in your configuration. By default, it is `false` in Git (i.e.,
+//mutable//) and `true` in Mercurial (i.e., //immutable//). The sections below
+explain how these settings affect workflows.
-A **liberal history mutability doctrine** means you rewrite local history. You
-develop in feature branches, but squash or amend before pushing by using ##git
-commit --amend## or ##git rebase -i##. Generally, one idea in the remote is
-represented by one commit.
+== History Mutability: Git ==
-A **conservative history mutability doctrine** means that you do not rewrite
-local history. This is similar to how Mercurial works. You develop in feature
-branches and push them without squashing commits. You do not use ##git commit
---amend## or ##git rebase -i##. Generally, one idea in the remote is represented
-by many commits.
+In a workflow with //mutable// history, you rewrite local history. You develop
+in feature branches, but squash or amend before pushing by using ##git commit
+--amend##, ##git rebase -i##, or `git merge --squash`. Generally, one idea in
+the remote is represented by one commit.
+
+In a workflow with //immutable// history, you do not rewrite local history. You
+develop in feature branches and push them without squashing commits. You do not
+use ##git commit --amend## or ##git rebase -i##. Generally, one idea in the
+remote is represented by many commits.
Practically, these are the differences you'll see based on your setting:
- **Mutable**
- `arc diff` will prompt you to amend lint changes into HEAD.
- `arc diff` will amend the commit message in HEAD after creating a
revision.
- `arc land` will default to the `--squash` strategy.
+ - `arc amend` will amend the commit message in HEAD with information from
+ the corresponding or specified Differential revision.
- **Immutable**
- `arc diff` will abort if it makes lint changes.
- `arc diff` will not amend the commit message in HEAD after creating a
revision.
- `arc land` will default to the `--merge` strategy.
+ - `arc amend` will exit with an error message.
+
+== History Mutability: Mercurial ==
+
+Before version 2.2, stock Mercurial has no history mutation commands, so
+this setting has no effect. With Mercurial 2.2. or newer, making history
+//mutable// means:
+
+ - **Mutable** (versions 2.2 and newer)
+ - `arc diff` will amend the commit message in `.` after creating a
+ revision.
+ - `arc amend` will amend the commit message in `.` with information from
+ the corresponding or specified Differential revision.
+ - **Immutable** (or versions prior to 2.2)
+ - `arc diff` will not amend the commit message in `.` after creating a
+ revision.
+ - `arc amend` will exit with an error message.
= How Libraries Are Located =
If you specify an external library to load, like 'examplelib', and use a
relative path like this:
{
...
- "load_libraries": {
- "examplelib" : "examplelib/src"
- },
+ "load": [
+ "examplelib/src"
+ ],
...
}
...arc looks for it by trying these paths:
- `path/to/root/examplelib/src/` First, arc looks in the project's root
directory (where the .arcconfig lives) to see if the library is part of
the project. This makes it easy to just put project-specific code in a
project.
- `path/to/root/../examplelib/src/` Next, arc looks //next to// the project's
root directory to see if the library is in a sibling directory. If you
work with several repositories, this makes it easy to put all the `arc`
code in one repository and just check it out in the same directory as
everything else.
- `php/include/path/examplelib/src` Finally, arc falls back to PHP, which
will look in paths described in the `include_path` php.ini setting. This
allows you to install libraries in some global location if you prefer.
You can alternately supply an absolute path, like `/var/arc/examplelib/src`, but
then everyone will need to install the library at that exact location.
NOTE: Specify the path to the directory which includes
`__phutil_library_init__.php`. For example, if your init file is in
`examplelib/src/__phutil_library_init__.php`, specify `examplelib/src`,
not just `examplelib/`.
The general intent here is:
- Put project-specific code in some directory in the project, like
`support/arc/src/`.
- Put shared code (e.g., which enforces general coding standards or hooks
up to unit tests or whatever) in a separate repository and check it out
next to other repositories.
- Or put everything in some standard location and add it to `include_path`.
diff --git a/src/docs/userguide/libraries.diviner b/src/docs/userguide/libraries.diviner
index 51275150e6..46bcff1b9a 100644
--- a/src/docs/userguide/libraries.diviner
+++ b/src/docs/userguide/libraries.diviner
@@ -1,156 +1,156 @@
@title libphutil Libraries User Guide
@group userguide
Guide to creating and managing libphutil libraries.
= Overview =
libphutil includes a library system which organizes PHP classes and functions
into modules. Some extensions and customizations of Arcanist and Phabricator
require you to make code available to Phabricator by providing it in a libphutil
library.
For example, if you want to store files in some kind of custom storage engine,
you need to write a class which can interact with that engine and then tell
Phabricator to load it.
In general, you perform these one-time setup steps:
- Create a new directory.
- Use ##arc liberate## to initialize and name the library.
- Add a dependency on Phabricator if necessary.
- Add the library to your Phabricator config or ##.arcconfig## so it will be
loaded at runtime.
Then, to add new code, you do this:
- Write or update classes.
- Update the library metadata by running ##arc liberate## again.
= Creating a New Library =
To **create a new libphutil library**:
$ mkdir libcustom/
$ cd libcustom/
libcustom/ $ arc liberate src/
Now you'll get a prompt like this:
lang=txt
No library currently exists at that path...
The directory '/some/path/libcustom/src' does not exist.
Do you want to create it? [y/N] y
Creating new libphutil library in '/some/path/libcustom/src'.
Choose a name for the new library.
What do you want to name this library?
Choose a library name (in this case, "libcustom" would be appropriate) and it
you should get some details about the library initialization:
lang=txt
Writing '__phutil_library_init__.php' to
'/some/path/libcustom/src/__phutil_library_init__.php'...
Using library root at 'src'...
Mapping library...
Verifying library...
Finalizing library map...
OKAY Library updated.
This will write three files:
- ##src/.phutil_module_cache## This is a cache which makes "arc liberate"
faster when you run it to update the library. You can safely remove it at
any time. If you check your library into version control, you can add this
file to ignore rules (like .gitignore).
- ##src/__phutil_library_init__.php## This records the name of the library and
tells libphutil that a library exists here.
- ##src/__phutil_library_map__.php## This is a map of all the symbols
(functions and classes) in the library, which allows them to be autoloaded
at runtime and dependencies to be statically managed by "arc liberate".
= Linking with Phabricator =
If you aren't using this library with Phabricator (e.g., you are only using it
with Arcanist or are building something else on libphutil) you can skip this
step.
But, if you intend to use this library with Phabricator, you need to define its
dependency on Phabricator by creating a ##.arcconfig## file which points at
Phabricator. For example, you might write this file to
##libcustom/.arcconfig##:
{
"project_id" : "libcustom",
- "phutil_libraries" : {
- "phabricator" : "phabricator/src/"
- }
+ "load" : [
+ "phabricator/src/"
+ ]
}
For details on creating a ##.arcconfig##, see
@{article:Arcanist User Guide: Configuring a New Project}. In general, this
tells ##arc liberate## that it should look for symbols in Phabricator when
performing static analysis.
NOTE: If Phabricator isn't located next to your custom library, specify a
path which actually points to the ##phabricator/## directory.
You do not need to declare dependencies on ##arcanist## or ##libphutil##,
since ##arc liberate## automatically loads them.
Finally, edit your Phabricator config to tell it to load your library at
runtime, by adding it to ##load-libraries##:
...
'load-libraries' => array(
'libcustom' => 'libcustom/src/',
),
...
Now, Phabricator will be able to load classes from your custom library.
= Writing Classes =
To actually write classes, create a new module and put code in it:
libcustom/ $ mkdir src/example/
libcustom/ $ nano src/example/ExampleClass.php # Edit some code.
Now, run ##arc liberate## to regenerate the static resource map:
libcustom/ $ arc liberate src/
This will automatically regenerate the static map of the library.
= What You Can Extend And Invoke =
libphutil, Arcanist and Phabricator are strict about extensibility of classes
and visibility of methods and properties. Most classes are marked ##final##, and
methods have the minimum required visibility (protected or private). The goal of
this strictness is to make it clear what you can safely extend, access, and
invoke, so your code will keep working as the upstream changes.
When developing libraries to work with libphutil, Arcanist and Phabricator, you
should respect method and property visibility and extend only classes marked
##@stable##. They are rendered with a large callout in the documentation (for
example: @{class@libphutil:AbstractDirectedGraph}). These classes are external
interfaces intended for extension.
If you want to extend a class but it is not marked ##@stable##, here are some
approaches you can take:
- Good: If possible, use composition rather than extension to build your
feature.
- Good: Check the documentation for a better way to accomplish what you're
trying to do.
- Good: Let us know what your use case is so we can make the class tree more
flexible or configurable, or point you at the right way to do whatever
you're trying to do, or explain why we don't let you do it.
- Discouraged: Send us a patch removing "final" (or turning "protected" or
"private" into "public"). We generally will not accept these patches, unless
there's a good reason that the current behavior is wrong.
- Discouraged: Create an ad-hoc local fork and remove "final" in your copy of
the code. This will make it more difficult for you to upgrade in the future.
- Discouraged: Use Reflection to violate visibility keywords.
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 16:46 (2 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1126617
Default Alt Text
(33 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment